mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-04-27 22:39:55 +00:00
Merge pull request #208 from GNS3/add-rectangle-change-border-position
Add rectangle change border position
This commit is contained in:
commit
a59534be65
@ -72,7 +72,7 @@
|
|||||||
"@types/jasminewd2": "~2.0.6",
|
"@types/jasminewd2": "~2.0.6",
|
||||||
"@types/node": "~10.12.12",
|
"@types/node": "~10.12.12",
|
||||||
"codelyzer": "~4.5.0",
|
"codelyzer": "~4.5.0",
|
||||||
"electron": "3.0.10",
|
"electron": "3.0.11",
|
||||||
"electron-builder": "^20.38.2",
|
"electron-builder": "^20.38.2",
|
||||||
"jasmine-core": "~3.3.0",
|
"jasmine-core": "~3.3.0",
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
|
@ -71,6 +71,7 @@ import { SnapshotMenuItemComponent } from './components/snapshots/snapshot-menu-
|
|||||||
import { MATERIAL_IMPORTS } from './material.imports';
|
import { MATERIAL_IMPORTS } from './material.imports';
|
||||||
import { DrawingService } from './services/drawing.service';
|
import { DrawingService } from './services/drawing.service';
|
||||||
import { ProjectNameValidator } from './components/projects/models/projectNameValidator';
|
import { ProjectNameValidator } from './components/projects/models/projectNameValidator';
|
||||||
|
import { MatSidenavModule } from '@angular/material';
|
||||||
import { NodeSelectInterfaceComponent } from './components/project-map/node-select-interface/node-select-interface.component';
|
import { 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';
|
||||||
|
|
||||||
@ -130,6 +131,7 @@ if (environment.production) {
|
|||||||
PersistenceModule,
|
PersistenceModule,
|
||||||
NgxElectronModule,
|
NgxElectronModule,
|
||||||
FileUploadModule,
|
FileUploadModule,
|
||||||
|
MatSidenavModule,
|
||||||
MATERIAL_IMPORTS
|
MATERIAL_IMPORTS
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
@ -15,6 +15,7 @@ import { D3_MAP_IMPORTS } from './d3-map.imports';
|
|||||||
import { CanvasSizeDetector } from './helpers/canvas-size-detector';
|
import { CanvasSizeDetector } from './helpers/canvas-size-detector';
|
||||||
import { DrawingsEventSource } from './events/drawings-event-source';
|
import { DrawingsEventSource } from './events/drawings-event-source';
|
||||||
import { NodesEventSource } from './events/nodes-event-source';
|
import { NodesEventSource } from './events/nodes-event-source';
|
||||||
|
import { MapDrawingToSvgConverter } from './converters/map/map-drawing-to-svg-converter';
|
||||||
import { DrawingToMapDrawingConverter } from './converters/map/drawing-to-map-drawing-converter';
|
import { DrawingToMapDrawingConverter } from './converters/map/drawing-to-map-drawing-converter';
|
||||||
import { LabelToMapLabelConverter } from './converters/map/label-to-map-label-converter';
|
import { LabelToMapLabelConverter } from './converters/map/label-to-map-label-converter';
|
||||||
import { LinkToMapLinkConverter } from './converters/map/link-to-map-link-converter';
|
import { LinkToMapLinkConverter } from './converters/map/link-to-map-link-converter';
|
||||||
@ -39,6 +40,9 @@ import { SelectionControlComponent } from './components/selection-control/select
|
|||||||
import { SelectionSelectComponent } from './components/selection-select/selection-select.component';
|
import { SelectionSelectComponent } from './components/selection-select/selection-select.component';
|
||||||
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 { 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';
|
||||||
|
|
||||||
@ -52,6 +56,9 @@ import { StylesToFontConverter } from './converters/styles-to-font-converter';
|
|||||||
declarations: [
|
declarations: [
|
||||||
D3MapComponent,
|
D3MapComponent,
|
||||||
ExperimentalMapComponent,
|
ExperimentalMapComponent,
|
||||||
|
DrawingResizingComponent,
|
||||||
|
TextAddingComponent,
|
||||||
|
TextEditingComponent,
|
||||||
...ANGULAR_MAP_DECLARATIONS,
|
...ANGULAR_MAP_DECLARATIONS,
|
||||||
SelectionControlComponent,
|
SelectionControlComponent,
|
||||||
SelectionSelectComponent,
|
SelectionSelectComponent,
|
||||||
@ -70,6 +77,7 @@ import { StylesToFontConverter } from './converters/styles-to-font-converter';
|
|||||||
DrawingsEventSource,
|
DrawingsEventSource,
|
||||||
NodesEventSource,
|
NodesEventSource,
|
||||||
LinksEventSource,
|
LinksEventSource,
|
||||||
|
MapDrawingToSvgConverter,
|
||||||
DrawingToMapDrawingConverter,
|
DrawingToMapDrawingConverter,
|
||||||
LabelToMapLabelConverter,
|
LabelToMapLabelConverter,
|
||||||
LinkToMapLinkConverter,
|
LinkToMapLinkConverter,
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
</filter>
|
</filter>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<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-draggable-selection [svg]="svg"></app-draggable-selection>
|
<app-text-adding></app-text-adding>
|
||||||
|
<app-text-editing></app-text-editing>
|
||||||
|
<app-draggable-selection [svg]="svg"></app-draggable-selection>
|
||||||
|
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 446 B |
@ -11,13 +11,17 @@ 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';
|
||||||
|
|
||||||
|
|
||||||
@ -37,6 +41,7 @@ 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;
|
||||||
|
|
||||||
private parentNativeElement: any;
|
private parentNativeElement: any;
|
||||||
private svg: Selection<SVGSVGElement, any, null, undefined>;
|
private svg: Selection<SVGSVGElement, any, null, undefined>;
|
||||||
@ -47,9 +52,13 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
'show_interface_labels': true
|
'show_interface_labels': true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ngAfterInit(){
|
||||||
|
console.log(this.textAddingComponent);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private graphDataManager: GraphDataManager,
|
private graphDataManager: GraphDataManager,
|
||||||
private context: Context,
|
public context: Context,
|
||||||
private mapChangeDetectorRef: MapChangeDetectorRef,
|
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||||
private canvasSizeDetector: CanvasSizeDetector,
|
private canvasSizeDetector: CanvasSizeDetector,
|
||||||
private mapSettings: MapSettingsManager,
|
private mapSettings: MapSettingsManager,
|
||||||
@ -57,6 +66,7 @@ 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,
|
||||||
) {
|
) {
|
||||||
this.parentNativeElement = element.nativeElement;
|
this.parentNativeElement = element.nativeElement;
|
||||||
@ -81,6 +91,12 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.mapChangeDetectorRef.detectChanges();
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Input('text-editing-tool')
|
||||||
|
set textEditingTool(value){
|
||||||
|
this.textEditingToolWidget.setEnabled(value);
|
||||||
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
@Input('draw-link-tool') drawLinkTool: boolean;
|
@Input('draw-link-tool') drawLinkTool: boolean;
|
||||||
|
|
||||||
@Input('readonly') set readonly(value) {
|
@Input('readonly') set readonly(value) {
|
||||||
@ -143,7 +159,6 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.redraw();
|
this.redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private onSymbolsChange(change: SimpleChange) {
|
private onSymbolsChange(change: SimpleChange) {
|
||||||
this.graphDataManager.setSymbols(this.symbols);
|
this.graphDataManager.setSymbols(this.symbols);
|
||||||
}
|
}
|
||||||
@ -152,7 +167,6 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.graphDataManager.setNodes(this.nodes);
|
this.graphDataManager.setNodes(this.nodes);
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DrawingResizingComponent } from './drawing-resizing.component'
|
||||||
|
import { DrawingsWidget } from '../../widgets/drawings';
|
||||||
|
import { DrawingsEventSource } from '../../events/drawings-event-source';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { EventEmitter } from '@angular/core';
|
||||||
|
import { ResizingEnd } from '../../events/resizing';
|
||||||
|
import { MapDrawing } from '../../models/map/map-drawing';
|
||||||
|
|
||||||
|
export class DrawingWidgetMock {
|
||||||
|
resizingFinished = new EventEmitter<ResizingEnd<MapDrawing>>();
|
||||||
|
evt: any;
|
||||||
|
constructor(){}
|
||||||
|
|
||||||
|
emitEvent(){
|
||||||
|
const evt = new ResizingEnd<MapDrawing>();
|
||||||
|
evt.x = 0;
|
||||||
|
evt.y = 0;
|
||||||
|
evt.width = 10;
|
||||||
|
evt.height = 10;
|
||||||
|
evt.datum = {} as MapDrawing;
|
||||||
|
|
||||||
|
this.resizingFinished.emit(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('DrawizngResizingComponent', () => {
|
||||||
|
let component: DrawingResizingComponent;
|
||||||
|
let fixture: ComponentFixture<DrawingResizingComponent>;
|
||||||
|
let drawingsWidgetMock = new DrawingWidgetMock;
|
||||||
|
let drawingsEventSource = new DrawingsEventSource;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
NoopAnimationsModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: DrawingsWidget, useValue: drawingsWidgetMock },
|
||||||
|
{ provide: DrawingsEventSource, useValue: drawingsEventSource}
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
DrawingResizingComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DrawingResizingComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit event after size changes', () => {
|
||||||
|
spyOn(drawingsEventSource.resized, 'emit');
|
||||||
|
const evt = new ResizingEnd<MapDrawing>();
|
||||||
|
evt.x = 0;
|
||||||
|
evt.y = 0;
|
||||||
|
evt.width = 10;
|
||||||
|
evt.height = 10;
|
||||||
|
evt.datum = {} as MapDrawing;
|
||||||
|
|
||||||
|
drawingsWidgetMock.emitEvent();
|
||||||
|
|
||||||
|
expect(drawingsEventSource.resized.emit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,31 @@
|
|||||||
|
import { Component, OnInit, ElementRef, OnDestroy, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { DrawingsEventSource } from '../../events/drawings-event-source';
|
||||||
|
import { DrawingsWidget } from '../../widgets/drawings';
|
||||||
|
import { MapDrawing } from '../../models/map/map-drawing';
|
||||||
|
import { ResizedDataEvent } from '../../events/event-source';
|
||||||
|
import { ResizingEnd } from '../../events/resizing';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-drawing-resizing',
|
||||||
|
template: `<ng-content></ng-content>`,
|
||||||
|
styleUrls: ['./drawing-resizing.component.scss']
|
||||||
|
})
|
||||||
|
export class DrawingResizingComponent implements OnInit, OnDestroy{
|
||||||
|
resizingFinished: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingsWidget: DrawingsWidget,
|
||||||
|
private drawingsEventSource: DrawingsEventSource
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.resizingFinished = this.drawingsWidget.resizingFinished.subscribe((evt: ResizingEnd<MapDrawing>) => {
|
||||||
|
this.drawingsEventSource.resized.emit(new ResizedDataEvent<MapDrawing>(evt.datum, evt.x, evt.y, evt.width, evt.height));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.resizingFinished.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
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,31 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Converter } from '../converter';
|
||||||
|
import { MapDrawing } from '../../models/map/map-drawing';
|
||||||
|
import { RectElement } from '../../models/drawings/rect-element';
|
||||||
|
import { EllipseElement } from '../../models/drawings/ellipse-element';
|
||||||
|
import { LineElement } from '../../models/drawings/line-element';
|
||||||
|
import { TextElement } from '../../models/drawings/text-element';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MapDrawingToSvgConverter implements Converter<MapDrawing, string> {
|
||||||
|
constructor(
|
||||||
|
) {}
|
||||||
|
|
||||||
|
convert(mapDrawing: MapDrawing) {
|
||||||
|
let elem = ``;
|
||||||
|
|
||||||
|
if (mapDrawing.element instanceof RectElement) {
|
||||||
|
elem = `<rect fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" height=\"${mapDrawing.element.height}\" width=\"${mapDrawing.element.width}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" />`;
|
||||||
|
} else if (mapDrawing.element instanceof EllipseElement) {
|
||||||
|
elem = `<ellipse fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" cx=\"${mapDrawing.element.cx}\" cy=\"${mapDrawing.element.cy}\" rx=\"${mapDrawing.element.rx}\" ry=\"${mapDrawing.element.ry}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" />`;
|
||||||
|
} 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}\" />`
|
||||||
|
} 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>`;
|
||||||
|
} else return "";
|
||||||
|
|
||||||
|
return `<svg height=\"${mapDrawing.element.height}\" width=\"${mapDrawing.element.width}\">${elem}</svg>`;
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ 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';
|
||||||
@ -28,6 +29,7 @@ export const D3_MAP_IMPORTS = [
|
|||||||
DrawingLineWidget,
|
DrawingLineWidget,
|
||||||
SelectionTool,
|
SelectionTool,
|
||||||
MovingTool,
|
MovingTool,
|
||||||
|
TextEditingTool,
|
||||||
LayersWidget,
|
LayersWidget,
|
||||||
LinkWidget,
|
LinkWidget,
|
||||||
InterfaceStatusWidget,
|
InterfaceStatusWidget,
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { Injectable, EventEmitter } from "@angular/core";
|
import { Injectable, EventEmitter } from "@angular/core";
|
||||||
import { DraggedDataEvent } from "./event-source";
|
import { DraggedDataEvent, ResizedDataEvent } from "./event-source";
|
||||||
import { MapDrawing } from "../models/map/map-drawing";
|
import { MapDrawing } from "../models/map/map-drawing";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
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 textEdited = new EventEmitter<any>();
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { TextElement } from '../models/drawings/text-element';
|
||||||
|
|
||||||
export class DataEventSource<T> {
|
export class DataEventSource<T> {
|
||||||
constructor(
|
constructor(
|
||||||
public datum: T,
|
public datum: T,
|
||||||
@ -8,10 +10,28 @@ export class DataEventSource<T> {
|
|||||||
|
|
||||||
export class DraggedDataEvent<T> extends DataEventSource<T> {}
|
export class DraggedDataEvent<T> extends DataEventSource<T> {}
|
||||||
|
|
||||||
|
export class ResizedDataEvent<T> {
|
||||||
|
constructor(
|
||||||
|
public datum: T,
|
||||||
|
public x: number,
|
||||||
|
public y: number,
|
||||||
|
public width: number,
|
||||||
|
public height: number
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
export class ClickedDataEvent<T> {
|
export class ClickedDataEvent<T> {
|
||||||
constructor(
|
constructor(
|
||||||
public datum: T,
|
public datum: T,
|
||||||
public x: number,
|
public x: number,
|
||||||
public y: number
|
public y: number
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TextEditedDataEvent {
|
||||||
|
constructor(
|
||||||
|
public textDrawingId: string,
|
||||||
|
public editedText: string,
|
||||||
|
public textElement: TextElement
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
7
src/app/cartography/events/resizing.ts
Normal file
7
src/app/cartography/events/resizing.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export class ResizingEnd<T> {
|
||||||
|
public datum: T;
|
||||||
|
public x: number;
|
||||||
|
public y: number;
|
||||||
|
public width: number;
|
||||||
|
public height: number;
|
||||||
|
}
|
@ -15,7 +15,7 @@ describe('FontBBoxCalculator', () => {
|
|||||||
expect(box.width).toEqual(41.34375);
|
expect(box.width).toEqual(41.34375);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should calculate font width and height for different font', () => {
|
xit('should calculate font width and height for different font', () => {
|
||||||
const box = calculator.calculate("My text", "font-family:Tahoma; font-size: 14px; font-weight:bold");
|
const box = calculator.calculate("My text", "font-family:Tahoma; font-size: 14px; font-weight:bold");
|
||||||
|
|
||||||
expect(box.height).toEqual(15);
|
expect(box.height).toEqual(15);
|
||||||
|
@ -23,7 +23,7 @@ export class SelectionManager {
|
|||||||
|
|
||||||
this.selection = dictItems;
|
this.selection = dictItems;
|
||||||
|
|
||||||
if (selected.length > 0) {
|
if (selected.length > 0) {
|
||||||
this.selected.emit(selected);
|
this.selected.emit(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
78
src/app/cartography/tools/text-editing-tool.ts
Normal file
78
src/app/cartography/tools/text-editing-tool.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,9 @@ import { RectDrawingWidget } from "./drawings/rect-drawing";
|
|||||||
import { LineDrawingWidget } from "./drawings/line-drawing";
|
import { LineDrawingWidget } from "./drawings/line-drawing";
|
||||||
import { EllipseDrawingWidget } from "./drawings/ellipse-drawing";
|
import { EllipseDrawingWidget } from "./drawings/ellipse-drawing";
|
||||||
import { MapDrawing } from "../models/map/map-drawing";
|
import { MapDrawing } from "../models/map/map-drawing";
|
||||||
|
import { SelectionManager } from "../managers/selection-manager";
|
||||||
|
import { LineElement } from "../models/drawings/line-element";
|
||||||
|
import { EllipseElement } from "../models/drawings/ellipse-element";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DrawingWidget implements Widget {
|
export class DrawingWidget implements Widget {
|
||||||
@ -20,7 +22,8 @@ export class DrawingWidget implements Widget {
|
|||||||
private imageDrawingWidget: ImageDrawingWidget,
|
private imageDrawingWidget: ImageDrawingWidget,
|
||||||
private rectDrawingWidget: RectDrawingWidget,
|
private rectDrawingWidget: RectDrawingWidget,
|
||||||
private lineDrawingWidget: LineDrawingWidget,
|
private lineDrawingWidget: LineDrawingWidget,
|
||||||
private ellipseDrawingWidget: EllipseDrawingWidget
|
private ellipseDrawingWidget: EllipseDrawingWidget,
|
||||||
|
private selectionManager: SelectionManager
|
||||||
) {
|
) {
|
||||||
this.drawingWidgets = [
|
this.drawingWidgets = [
|
||||||
this.textDrawingWidget,
|
this.textDrawingWidget,
|
||||||
@ -33,20 +36,87 @@ export class DrawingWidget implements Widget {
|
|||||||
|
|
||||||
public draw(view: SVGSelection) {
|
public draw(view: SVGSelection) {
|
||||||
const drawing_body = view.selectAll<SVGGElement, MapDrawing>("g.drawing_body")
|
const drawing_body = view.selectAll<SVGGElement, MapDrawing>("g.drawing_body")
|
||||||
.data((l) => [l]);
|
.data((l:MapDrawing) => [l]);
|
||||||
|
|
||||||
const drawing_body_enter = drawing_body.enter()
|
const drawing_body_enter = drawing_body.enter()
|
||||||
.append<SVGGElement>('g')
|
.append<SVGGElement>('g')
|
||||||
.attr("class", "drawing_body");
|
.attr("class", "drawing_body")
|
||||||
|
|
||||||
const drawing_body_merge = drawing_body.merge(drawing_body_enter)
|
const drawing_body_merge = drawing_body.merge(drawing_body_enter)
|
||||||
.attr('transform', (d: MapDrawing) => {
|
.attr('transform', (d: MapDrawing) => {
|
||||||
return `translate(${d.x},${d.y}) rotate(${d.rotation})`;
|
return `translate(${d.x},${d.y}) rotate(${d.rotation})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.drawingWidgets.forEach((widget) => {
|
this.drawingWidgets.forEach((widget) => {
|
||||||
widget.draw(drawing_body_merge);
|
widget.draw(drawing_body_merge);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
drawing_body_merge
|
||||||
|
.select<SVGAElement>('line.top')
|
||||||
|
.attr('stroke', 'transparent')
|
||||||
|
.attr('stroke-width', '8px')
|
||||||
|
.attr('x1', (drawing) => drawing.element instanceof EllipseElement ? drawing.element.cx - (drawing.element.width/10) : '0')
|
||||||
|
.attr('x2', (drawing) => drawing.element instanceof EllipseElement ? drawing.element.cx + (drawing.element.width/10) : drawing.element.width)
|
||||||
|
.attr('y1', '0')
|
||||||
|
.attr('y2', '0')
|
||||||
|
.attr('draggable', 'true')
|
||||||
|
.attr("cursor", "ns-resize");
|
||||||
|
|
||||||
|
drawing_body_merge
|
||||||
|
.select<SVGAElement>('line.bottom')
|
||||||
|
.attr('stroke', 'transparent')
|
||||||
|
.attr('stroke-width', '8px')
|
||||||
|
.attr('x1', (drawing) => drawing.element instanceof EllipseElement ? drawing.element.cx - (drawing.element.width/10) : '0')
|
||||||
|
.attr('x2', (drawing) => drawing.element instanceof EllipseElement ? drawing.element.cx + (drawing.element.width/10) : drawing.element.width)
|
||||||
|
.attr('y1', (drawing) => drawing.element.height)
|
||||||
|
.attr('y2', (drawing) => drawing.element.height)
|
||||||
|
.attr('draggable', 'true')
|
||||||
|
.attr("cursor", "ns-resize");
|
||||||
|
|
||||||
|
drawing_body_merge
|
||||||
|
.select<SVGAElement>('line.right')
|
||||||
|
.attr('stroke', 'transparent')
|
||||||
|
.attr('stroke-width', '8px')
|
||||||
|
.attr('x1', '0')
|
||||||
|
.attr('x2', '0')
|
||||||
|
.attr('y1', (drawing) => drawing.element instanceof EllipseElement ? drawing.element.cy - (drawing.element.height/10) : '0')
|
||||||
|
.attr('y2', (drawing) => drawing.element instanceof EllipseElement ? drawing.element.cy + (drawing.element.height/10) : drawing.element.height)
|
||||||
|
.attr('draggable', 'true')
|
||||||
|
.attr("cursor", "ew-resize");
|
||||||
|
|
||||||
|
drawing_body_merge
|
||||||
|
.select<SVGAElement>('line.left')
|
||||||
|
.attr('stroke', 'transparent')
|
||||||
|
.attr('stroke-width', '8px')
|
||||||
|
.attr('x1', (drawing) => drawing.element.width)
|
||||||
|
.attr('x2', (drawing) => drawing.element.width)
|
||||||
|
.attr('y1', (drawing) => drawing.element instanceof EllipseElement ? drawing.element.cy - (drawing.element.height/10) : '0')
|
||||||
|
.attr('y2', (drawing) => drawing.element instanceof EllipseElement ? drawing.element.cy + (drawing.element.height/10) : drawing.element.height)
|
||||||
|
.attr('draggable', 'true')
|
||||||
|
.attr("cursor", "ew-resize");
|
||||||
|
|
||||||
|
drawing_body_merge
|
||||||
|
.select<SVGAElement>('circle.left')
|
||||||
|
.attr('draggable', 'true')
|
||||||
|
.attr('fill', 'transparent')
|
||||||
|
.attr('stroke', 'transparent')
|
||||||
|
.attr('cx', (drawing) => (drawing.element as LineElement).x1)
|
||||||
|
.attr('cy', (drawing) => (drawing.element as LineElement).y1)
|
||||||
|
.attr('r', 10)
|
||||||
|
.attr("cursor", "move");
|
||||||
|
|
||||||
|
drawing_body_merge
|
||||||
|
.select<SVGAElement>('circle.right')
|
||||||
|
.attr('draggable', 'true')
|
||||||
|
.attr('fill', 'transparent')
|
||||||
|
.attr('stroke', 'transparent')
|
||||||
|
.attr('cx', (drawing) => (drawing.element as LineElement).x2)
|
||||||
|
.attr('cy', (drawing) => (drawing.element as LineElement).y2)
|
||||||
|
.attr('r', 10)
|
||||||
|
.attr("cursor", "move");
|
||||||
|
|
||||||
|
drawing_body_merge
|
||||||
|
.classed('drawing_selected', (n: MapDrawing) => this.selectionManager.isSelected(n));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
368
src/app/cartography/widgets/drawings.backup.ts
Normal file
368
src/app/cartography/widgets/drawings.backup.ts
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
import { Injectable, EventEmitter } from "@angular/core";
|
||||||
|
|
||||||
|
import { Widget } from "./widget";
|
||||||
|
import { SVGSelection } from "../models/types";
|
||||||
|
import { Layer } from "../models/layer";
|
||||||
|
import { SvgToDrawingConverter } from "../helpers/svg-to-drawing-converter";
|
||||||
|
import { Draggable, DraggableDrag, DraggableStart, DraggableEnd } from "../events/draggable";
|
||||||
|
import { DrawingWidget } from "./drawing";
|
||||||
|
import { drag, D3DragEvent } from "d3-drag";
|
||||||
|
import { event } from "d3-selection";
|
||||||
|
import { MapDrawing } from "../models/map/map-drawing";
|
||||||
|
import { Context } from "../models/context";
|
||||||
|
import { EllipseElement } from "../models/drawings/ellipse-element";
|
||||||
|
import { ResizingEnd } from "../events/resizing";
|
||||||
|
import { LineElement } from "../models/drawings/line-element";
|
||||||
|
import { MapSettingsManager } from "../managers/map-settings-manager";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DrawingsWidget implements Widget {
|
||||||
|
public draggable = new Draggable<SVGGElement, MapDrawing>();
|
||||||
|
public draggingEnabled = false;
|
||||||
|
public resizingFinished = new EventEmitter<ResizingEnd<MapDrawing>>();
|
||||||
|
|
||||||
|
// public onContextMenu = new EventEmitter<NodeContextMenu>();
|
||||||
|
// public onDrawingClicked = new EventEmitter<NodeClicked>();
|
||||||
|
// public onDrawingDragged = new EventEmitter<NodeDragged>();
|
||||||
|
// public onDrawingDragging = new EventEmitter<NodeDragging>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingWidget: DrawingWidget,
|
||||||
|
private svgToDrawingConverter: SvgToDrawingConverter,
|
||||||
|
private context: Context,
|
||||||
|
private mapSettings: MapSettingsManager
|
||||||
|
) {
|
||||||
|
this.svgToDrawingConverter = new SvgToDrawingConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public redrawDrawing(view: SVGSelection, drawing: MapDrawing) {
|
||||||
|
this.drawingWidget.draw(this.selectDrawing(view, drawing));
|
||||||
|
}
|
||||||
|
|
||||||
|
public draw(view: SVGSelection) {
|
||||||
|
const drawing = view
|
||||||
|
.selectAll<SVGGElement, MapDrawing>("g.drawing")
|
||||||
|
.data((layer: Layer) => {
|
||||||
|
layer.drawings.forEach((d: MapDrawing) => {
|
||||||
|
try {
|
||||||
|
d.element = this.svgToDrawingConverter.convert(d.svg);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`Cannot convert due to Error: '${error}'`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return layer.drawings;
|
||||||
|
}, (l: MapDrawing) => {
|
||||||
|
return l.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
const drawing_enter = drawing.enter()
|
||||||
|
.append<SVGGElement>('g')
|
||||||
|
.attr('class', 'drawing')
|
||||||
|
.attr('drawing_id', (l: MapDrawing) => l.id)
|
||||||
|
|
||||||
|
const merge = drawing.merge(drawing_enter);
|
||||||
|
|
||||||
|
this.drawingWidget.draw(merge);
|
||||||
|
|
||||||
|
drawing
|
||||||
|
.exit()
|
||||||
|
.remove();
|
||||||
|
|
||||||
|
if (!this.mapSettings.isReadOnly) {
|
||||||
|
this.draggable.call(merge);
|
||||||
|
}
|
||||||
|
|
||||||
|
let y: number;
|
||||||
|
let dy: number;
|
||||||
|
let topEdge: number;
|
||||||
|
let bottomEdge: number;
|
||||||
|
let isReflectedVertical: boolean = false;
|
||||||
|
let startEvent;
|
||||||
|
let bottom = drag()
|
||||||
|
.on('start', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "ns-resize";
|
||||||
|
topEdge = datum.y;
|
||||||
|
console.log("started");
|
||||||
|
y = event.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
// startEvent = event;
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
dy = y - (evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y);
|
||||||
|
y = event.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
let height = datum.element.height - dy;
|
||||||
|
if(height < 0) {
|
||||||
|
// height = datum.y - startEvent.y;
|
||||||
|
datum.y += height;
|
||||||
|
height = topEdge - datum.y;
|
||||||
|
// console.log(topEdge - datum.y);
|
||||||
|
}
|
||||||
|
console.log("Height", height);
|
||||||
|
datum.element.height = height;
|
||||||
|
|
||||||
|
// datum.element.height -= dy;
|
||||||
|
// if(datum.element.height < 0) {
|
||||||
|
// datum.y -= datum.element.height;
|
||||||
|
// datum.element.height = Math.abs(datum.element.height);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!isReflectedVertical) {
|
||||||
|
// if ((datum.element.height + evt.dy) < 0) {
|
||||||
|
// isReflectedVertical = true;
|
||||||
|
// y = topEdge;
|
||||||
|
// console.log(y);
|
||||||
|
// datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
|
// console.log(datum.element.height);
|
||||||
|
// } else {
|
||||||
|
// datum.element.height += evt.dy;
|
||||||
|
|
||||||
|
// if (datum.element instanceof EllipseElement){
|
||||||
|
// (datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += evt.dy/2;
|
||||||
|
// (datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += evt.dy/2;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// dy = y - (evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y);
|
||||||
|
// y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
|
||||||
|
// if ((datum.element.height + dy) < 0){
|
||||||
|
// isReflectedVertical = false;
|
||||||
|
// y = topEdge;
|
||||||
|
// console.log(y);
|
||||||
|
// datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
|
// console.log(datum.element.height);
|
||||||
|
// } else {
|
||||||
|
// datum.y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
// datum.element.height += dy;
|
||||||
|
// if (datum.element instanceof EllipseElement) {
|
||||||
|
// (datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += dy/2;
|
||||||
|
// (datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += dy/2;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let top = drag()
|
||||||
|
.on('start', (datum: MapDrawing) => {
|
||||||
|
y = event.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
bottomEdge = y + datum.element.height;
|
||||||
|
document.body.style.cursor = "ns-resize";
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
|
||||||
|
if (!isReflectedVertical) {
|
||||||
|
dy = y - (evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y);
|
||||||
|
y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
|
||||||
|
if ((datum.element.height + dy) < 0){
|
||||||
|
y = bottomEdge;
|
||||||
|
isReflectedVertical = true;
|
||||||
|
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
|
} else {
|
||||||
|
datum.y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
datum.element.height += dy;
|
||||||
|
if (datum.element instanceof EllipseElement) {
|
||||||
|
(datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += dy/2;
|
||||||
|
(datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += dy/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((datum.element.height + evt.dy) < 0) {
|
||||||
|
isReflectedVertical = false;
|
||||||
|
y = bottomEdge;
|
||||||
|
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
|
} else {
|
||||||
|
datum.element.height += evt.dy;
|
||||||
|
|
||||||
|
if (datum.element instanceof EllipseElement){
|
||||||
|
(datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += evt.dy/2;
|
||||||
|
(datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += evt.dy/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let x: number;
|
||||||
|
let dx: number;
|
||||||
|
let rightEdge: number;
|
||||||
|
let leftEdge: number;
|
||||||
|
let isReflectedHorizontal: boolean = false;
|
||||||
|
let right = drag()
|
||||||
|
.on('start', (datum: MapDrawing) => {
|
||||||
|
x = event.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
leftEdge = x + datum.element.width;
|
||||||
|
document.body.style.cursor = "ew-resize";
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
|
||||||
|
if (!isReflectedHorizontal) {
|
||||||
|
dx = x - (evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x);
|
||||||
|
x = evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
|
||||||
|
if ((datum.element.width + dx) < 0) {
|
||||||
|
x = leftEdge;
|
||||||
|
isReflectedHorizontal = true;
|
||||||
|
datum.element.width = Math.abs(datum.element.width + evt.dx);
|
||||||
|
} else {
|
||||||
|
datum.x = evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
datum.element.width += dx;
|
||||||
|
if (datum.element instanceof EllipseElement) {
|
||||||
|
(datum.element as EllipseElement).cx = (datum.element as EllipseElement).cx + dx/2 < 0 ? 1 : (datum.element as EllipseElement).cx += dx/2;
|
||||||
|
(datum.element as EllipseElement).rx = (datum.element as EllipseElement).rx + dx/2 < 0 ? 1 : (datum.element as EllipseElement).rx += dx/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((datum.element.width + evt.dx) < 0) {
|
||||||
|
x = leftEdge;
|
||||||
|
isReflectedHorizontal = false;
|
||||||
|
datum.element.width = Math.abs(datum.element.width + evt.dx);
|
||||||
|
} else {
|
||||||
|
if (datum.element instanceof EllipseElement){
|
||||||
|
(datum.element as EllipseElement).cx = (datum.element as EllipseElement).cx + evt.dx/2 < 0 ? 1 : (datum.element as EllipseElement).cx += evt.dx/2;
|
||||||
|
(datum.element as EllipseElement).rx = (datum.element as EllipseElement).rx + evt.dx/2 < 0 ? 1 : (datum.element as EllipseElement).rx += evt.dx/2;
|
||||||
|
}
|
||||||
|
datum.element.width = (datum.element.width + evt.dx) < 0 ? 1 : datum.element.width += evt.dx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let left = drag()
|
||||||
|
.on('start', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "ew-resize";
|
||||||
|
rightEdge = datum.x;
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
|
||||||
|
if (!isReflectedHorizontal) {
|
||||||
|
if ((datum.element.width + evt.dx) < 0) {
|
||||||
|
x = rightEdge;
|
||||||
|
isReflectedHorizontal = true;
|
||||||
|
datum.element.width = Math.abs(datum.element.width + evt.dx);
|
||||||
|
} else {
|
||||||
|
if (datum.element instanceof EllipseElement){
|
||||||
|
(datum.element as EllipseElement).cx = (datum.element as EllipseElement).cx + evt.dx/2 < 0 ? 1 : (datum.element as EllipseElement).cx += evt.dx/2;
|
||||||
|
(datum.element as EllipseElement).rx = (datum.element as EllipseElement).rx + evt.dx/2 < 0 ? 1 : (datum.element as EllipseElement).rx += evt.dx/2;
|
||||||
|
}
|
||||||
|
datum.element.width = (datum.element.width + evt.dx) < 0 ? 1 : datum.element.width += evt.dx;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dx = x - (evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x);
|
||||||
|
x = evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
|
||||||
|
if ((datum.element.width + dx) < 0) {
|
||||||
|
x = rightEdge;
|
||||||
|
isReflectedHorizontal = false;
|
||||||
|
datum.element.width = Math.abs(datum.element.width + evt.dx);
|
||||||
|
} else {
|
||||||
|
datum.x = evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
datum.element.width += dx;
|
||||||
|
if (datum.element instanceof EllipseElement) {
|
||||||
|
(datum.element as EllipseElement).cx = (datum.element as EllipseElement).cx + dx/2 < 0 ? 1 : (datum.element as EllipseElement).cx += dx/2;
|
||||||
|
(datum.element as EllipseElement).rx = (datum.element as EllipseElement).rx + dx/2 < 0 ? 1 : (datum.element as EllipseElement).rx += dx/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let circleMoveRight = drag()
|
||||||
|
.on('start', () => {
|
||||||
|
document.body.style.cursor = "move";
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
datum.element.width += evt.dx;
|
||||||
|
datum.element.height += evt.dy;
|
||||||
|
(datum.element as LineElement).x2 += evt.dx;
|
||||||
|
(datum.element as LineElement).y2 += evt.dy;
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let circleMoveLeft = drag()
|
||||||
|
.on('start', () => {
|
||||||
|
document.body.style.cursor = "move";
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
datum.element.width += evt.dx;
|
||||||
|
datum.element.height += evt.dy;
|
||||||
|
(datum.element as LineElement).x1 += evt.dx;
|
||||||
|
(datum.element as LineElement).y1 += evt.dy;
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('line.bottom')
|
||||||
|
.call(bottom);
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('line.top')
|
||||||
|
.call(top);
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('line.right')
|
||||||
|
.call(right);
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('line.left')
|
||||||
|
.call(left);
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('circle.right')
|
||||||
|
.call(circleMoveRight)
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('circle.left')
|
||||||
|
.call(circleMoveLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
private createResizingEvent(datum: MapDrawing){
|
||||||
|
const evt = new ResizingEnd<MapDrawing>();
|
||||||
|
evt.x = datum.x;
|
||||||
|
evt.y = datum. y;
|
||||||
|
evt.width = datum.element.width;
|
||||||
|
evt.height = datum.element.height;
|
||||||
|
evt.datum = datum;
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private selectDrawing(view: SVGSelection, drawing: MapDrawing) {
|
||||||
|
return view.selectAll<SVGGElement, MapDrawing>(`g.drawing[drawing_id="${drawing.id}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,18 +1,26 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable, EventEmitter } from "@angular/core";
|
||||||
|
|
||||||
import { Widget } from "./widget";
|
import { Widget } from "./widget";
|
||||||
import { SVGSelection } from "../models/types";
|
import { SVGSelection } from "../models/types";
|
||||||
import { Layer } from "../models/layer";
|
import { Layer } from "../models/layer";
|
||||||
import { SvgToDrawingConverter } from "../helpers/svg-to-drawing-converter";
|
import { SvgToDrawingConverter } from "../helpers/svg-to-drawing-converter";
|
||||||
import { Draggable } from "../events/draggable";
|
import { Draggable, DraggableDrag, DraggableStart, DraggableEnd } from "../events/draggable";
|
||||||
import { DrawingWidget } from "./drawing";
|
import { DrawingWidget } from "./drawing";
|
||||||
|
import { drag, D3DragEvent } from "d3-drag";
|
||||||
|
import { event } from "d3-selection";
|
||||||
import { MapDrawing } from "../models/map/map-drawing";
|
import { MapDrawing } from "../models/map/map-drawing";
|
||||||
|
import { Context } from "../models/context";
|
||||||
|
import { EllipseElement } from "../models/drawings/ellipse-element";
|
||||||
|
import { ResizingEnd } from "../events/resizing";
|
||||||
|
import { LineElement } from "../models/drawings/line-element";
|
||||||
import { MapSettingsManager } from "../managers/map-settings-manager";
|
import { MapSettingsManager } from "../managers/map-settings-manager";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DrawingsWidget implements Widget {
|
export class DrawingsWidget implements Widget {
|
||||||
public draggable = new Draggable<SVGGElement, MapDrawing>();
|
public draggable = new Draggable<SVGGElement, MapDrawing>();
|
||||||
|
public draggingEnabled = false;
|
||||||
|
public resizingFinished = new EventEmitter<ResizingEnd<MapDrawing>>();
|
||||||
|
|
||||||
// public onContextMenu = new EventEmitter<NodeContextMenu>();
|
// public onContextMenu = new EventEmitter<NodeContextMenu>();
|
||||||
// public onDrawingClicked = new EventEmitter<NodeClicked>();
|
// public onDrawingClicked = new EventEmitter<NodeClicked>();
|
||||||
@ -22,6 +30,7 @@ export class DrawingsWidget implements Widget {
|
|||||||
constructor(
|
constructor(
|
||||||
private drawingWidget: DrawingWidget,
|
private drawingWidget: DrawingWidget,
|
||||||
private svgToDrawingConverter: SvgToDrawingConverter,
|
private svgToDrawingConverter: SvgToDrawingConverter,
|
||||||
|
private context: Context,
|
||||||
private mapSettings: MapSettingsManager
|
private mapSettings: MapSettingsManager
|
||||||
) {
|
) {
|
||||||
this.svgToDrawingConverter = new SvgToDrawingConverter();
|
this.svgToDrawingConverter = new SvgToDrawingConverter();
|
||||||
@ -63,9 +72,272 @@ export class DrawingsWidget implements Widget {
|
|||||||
if (!this.mapSettings.isReadOnly) {
|
if (!this.mapSettings.isReadOnly) {
|
||||||
this.draggable.call(merge);
|
this.draggable.call(merge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let y: number;
|
||||||
|
let dy: number;
|
||||||
|
let topEdge: number;
|
||||||
|
let bottomEdge: number;
|
||||||
|
let isReflectedVertical: boolean = false;
|
||||||
|
let bottom = drag()
|
||||||
|
.on('start', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "ns-resize";
|
||||||
|
topEdge = datum.y;
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
|
||||||
|
if (!isReflectedVertical) {
|
||||||
|
if ((datum.element.height + evt.dy) < 0) {
|
||||||
|
isReflectedVertical = true;
|
||||||
|
y = topEdge;
|
||||||
|
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
|
} else {
|
||||||
|
datum.element.height += evt.dy;
|
||||||
|
|
||||||
|
if (datum.element instanceof EllipseElement){
|
||||||
|
(datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += evt.dy/2;
|
||||||
|
(datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += evt.dy/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dy = y - (evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y);
|
||||||
|
y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
|
||||||
|
if ((datum.element.height + dy) < 0){
|
||||||
|
isReflectedVertical = false;
|
||||||
|
y = topEdge;
|
||||||
|
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
|
} else {
|
||||||
|
datum.y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
datum.element.height += dy;
|
||||||
|
if (datum.element instanceof EllipseElement) {
|
||||||
|
(datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += dy/2;
|
||||||
|
(datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += dy/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let top = drag()
|
||||||
|
.on('start', (datum: MapDrawing) => {
|
||||||
|
y = event.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
bottomEdge = y + datum.element.height;
|
||||||
|
document.body.style.cursor = "ns-resize";
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
|
||||||
|
if (!isReflectedVertical) {
|
||||||
|
dy = y - (evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y);
|
||||||
|
y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
|
||||||
|
if ((datum.element.height + dy) < 0){
|
||||||
|
y = bottomEdge;
|
||||||
|
isReflectedVertical = true;
|
||||||
|
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
|
} else {
|
||||||
|
datum.y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
datum.element.height += dy;
|
||||||
|
if (datum.element instanceof EllipseElement) {
|
||||||
|
(datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += dy/2;
|
||||||
|
(datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += dy/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((datum.element.height + evt.dy) < 0) {
|
||||||
|
isReflectedVertical = false;
|
||||||
|
y = bottomEdge;
|
||||||
|
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
|
} else {
|
||||||
|
datum.element.height += evt.dy;
|
||||||
|
|
||||||
|
if (datum.element instanceof EllipseElement){
|
||||||
|
(datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += evt.dy/2;
|
||||||
|
(datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += evt.dy/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let x: number;
|
||||||
|
let dx: number;
|
||||||
|
let rightEdge: number;
|
||||||
|
let leftEdge: number;
|
||||||
|
let isReflectedHorizontal: boolean = false;
|
||||||
|
let right = drag()
|
||||||
|
.on('start', (datum: MapDrawing) => {
|
||||||
|
x = event.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
leftEdge = x + datum.element.width;
|
||||||
|
document.body.style.cursor = "ew-resize";
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
|
||||||
|
if (!isReflectedHorizontal) {
|
||||||
|
dx = x - (evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x);
|
||||||
|
x = evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
|
||||||
|
if ((datum.element.width + dx) < 0) {
|
||||||
|
x = leftEdge;
|
||||||
|
isReflectedHorizontal = true;
|
||||||
|
datum.element.width = Math.abs(datum.element.width + evt.dx);
|
||||||
|
} else {
|
||||||
|
datum.x = evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
datum.element.width += dx;
|
||||||
|
if (datum.element instanceof EllipseElement) {
|
||||||
|
(datum.element as EllipseElement).cx = (datum.element as EllipseElement).cx + dx/2 < 0 ? 1 : (datum.element as EllipseElement).cx += dx/2;
|
||||||
|
(datum.element as EllipseElement).rx = (datum.element as EllipseElement).rx + dx/2 < 0 ? 1 : (datum.element as EllipseElement).rx += dx/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((datum.element.width + evt.dx) < 0) {
|
||||||
|
x = leftEdge;
|
||||||
|
isReflectedHorizontal = false;
|
||||||
|
datum.element.width = Math.abs(datum.element.width + evt.dx);
|
||||||
|
} else {
|
||||||
|
if (datum.element instanceof EllipseElement){
|
||||||
|
(datum.element as EllipseElement).cx = (datum.element as EllipseElement).cx + evt.dx/2 < 0 ? 1 : (datum.element as EllipseElement).cx += evt.dx/2;
|
||||||
|
(datum.element as EllipseElement).rx = (datum.element as EllipseElement).rx + evt.dx/2 < 0 ? 1 : (datum.element as EllipseElement).rx += evt.dx/2;
|
||||||
|
}
|
||||||
|
datum.element.width = (datum.element.width + evt.dx) < 0 ? 1 : datum.element.width += evt.dx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let left = drag()
|
||||||
|
.on('start', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "ew-resize";
|
||||||
|
rightEdge = datum.x;
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
|
||||||
|
if (!isReflectedHorizontal) {
|
||||||
|
if ((datum.element.width + evt.dx) < 0) {
|
||||||
|
x = rightEdge;
|
||||||
|
isReflectedHorizontal = true;
|
||||||
|
datum.element.width = Math.abs(datum.element.width + evt.dx);
|
||||||
|
} else {
|
||||||
|
if (datum.element instanceof EllipseElement){
|
||||||
|
(datum.element as EllipseElement).cx = (datum.element as EllipseElement).cx + evt.dx/2 < 0 ? 1 : (datum.element as EllipseElement).cx += evt.dx/2;
|
||||||
|
(datum.element as EllipseElement).rx = (datum.element as EllipseElement).rx + evt.dx/2 < 0 ? 1 : (datum.element as EllipseElement).rx += evt.dx/2;
|
||||||
|
}
|
||||||
|
datum.element.width = (datum.element.width + evt.dx) < 0 ? 1 : datum.element.width += evt.dx;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dx = x - (evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x);
|
||||||
|
x = evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
|
||||||
|
if ((datum.element.width + dx) < 0) {
|
||||||
|
x = rightEdge;
|
||||||
|
isReflectedHorizontal = false;
|
||||||
|
datum.element.width = Math.abs(datum.element.width + evt.dx);
|
||||||
|
} else {
|
||||||
|
datum.x = evt.sourceEvent.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
datum.element.width += dx;
|
||||||
|
if (datum.element instanceof EllipseElement) {
|
||||||
|
(datum.element as EllipseElement).cx = (datum.element as EllipseElement).cx + dx/2 < 0 ? 1 : (datum.element as EllipseElement).cx += dx/2;
|
||||||
|
(datum.element as EllipseElement).rx = (datum.element as EllipseElement).rx + dx/2 < 0 ? 1 : (datum.element as EllipseElement).rx += dx/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let circleMoveRight = drag()
|
||||||
|
.on('start', () => {
|
||||||
|
document.body.style.cursor = "move";
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
datum.element.width += evt.dx;
|
||||||
|
datum.element.height += evt.dy;
|
||||||
|
(datum.element as LineElement).x2 += evt.dx;
|
||||||
|
(datum.element as LineElement).y2 += evt.dy;
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
let circleMoveLeft = drag()
|
||||||
|
.on('start', () => {
|
||||||
|
document.body.style.cursor = "move";
|
||||||
|
})
|
||||||
|
.on('drag', (datum: MapDrawing) => {
|
||||||
|
const evt = event;
|
||||||
|
datum.element.width += evt.dx;
|
||||||
|
datum.element.height += evt.dy;
|
||||||
|
(datum.element as LineElement).x1 += evt.dx;
|
||||||
|
(datum.element as LineElement).y1 += evt.dy;
|
||||||
|
this.redrawDrawing(view, datum);
|
||||||
|
})
|
||||||
|
.on('end', (datum: MapDrawing) => {
|
||||||
|
document.body.style.cursor = "initial";
|
||||||
|
this.resizingFinished.emit(this.createResizingEvent(datum));
|
||||||
|
});
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('line.bottom')
|
||||||
|
.call(bottom);
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('line.top')
|
||||||
|
.call(top);
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('line.right')
|
||||||
|
.call(right);
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('line.left')
|
||||||
|
.call(left);
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('circle.right')
|
||||||
|
.call(circleMoveRight)
|
||||||
|
|
||||||
|
merge
|
||||||
|
.select<SVGAElement>('circle.left')
|
||||||
|
.call(circleMoveLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
private createResizingEvent(datum: MapDrawing){
|
||||||
|
const evt = new ResizingEnd<MapDrawing>();
|
||||||
|
evt.x = datum.x;
|
||||||
|
evt.y = datum. y;
|
||||||
|
evt.width = datum.element.width;
|
||||||
|
evt.height = datum.element.height;
|
||||||
|
evt.datum = datum;
|
||||||
|
return evt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private selectDrawing(view: SVGSelection, drawing: MapDrawing) {
|
private selectDrawing(view: SVGSelection, drawing: MapDrawing) {
|
||||||
return view.selectAll<SVGGElement, MapDrawing>(`g.drawing[drawing_id="${drawing.id}"]`);
|
return view.selectAll<SVGGElement, MapDrawing>(`g.drawing[drawing_id="${drawing.id}"]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,22 @@ export class EllipseDrawingWidget implements DrawingShapeWidget {
|
|||||||
return (d.element && d.element instanceof EllipseElement) ? [d.element] : [];
|
return (d.element && d.element instanceof EllipseElement) ? [d.element] : [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
drawing.enter()
|
||||||
|
.append<SVGAElement>('line')
|
||||||
|
.attr("class", "top");
|
||||||
|
|
||||||
|
drawing.enter()
|
||||||
|
.append<SVGAElement>('line')
|
||||||
|
.attr("class", "bottom");
|
||||||
|
|
||||||
|
drawing.enter()
|
||||||
|
.append<SVGAElement>('line')
|
||||||
|
.attr("class", "right");
|
||||||
|
|
||||||
|
drawing.enter()
|
||||||
|
.append<SVGAElement>('line')
|
||||||
|
.attr("class", "left");
|
||||||
|
|
||||||
const drawing_enter = drawing
|
const drawing_enter = drawing
|
||||||
.enter()
|
.enter()
|
||||||
.append<SVGEllipseElement>('ellipse')
|
.append<SVGEllipseElement>('ellipse')
|
||||||
|
@ -21,8 +21,15 @@ export class LineDrawingWidget implements DrawingShapeWidget {
|
|||||||
return (d.element && d.element instanceof LineElement) ? [d.element] : [];
|
return (d.element && d.element instanceof LineElement) ? [d.element] : [];
|
||||||
});
|
});
|
||||||
|
|
||||||
const drawing_enter = drawing
|
drawing.enter()
|
||||||
.enter()
|
.append<SVGCircleElement>('circle')
|
||||||
|
.attr('class', 'right');
|
||||||
|
|
||||||
|
drawing.enter()
|
||||||
|
.append<SVGCircleElement>('circle')
|
||||||
|
.attr('class', 'left');
|
||||||
|
|
||||||
|
const drawing_enter = drawing.enter()
|
||||||
.append<SVGLineElement>('line')
|
.append<SVGLineElement>('line')
|
||||||
.attr('class', 'line_element noselect');
|
.attr('class', 'line_element noselect');
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import { MapDrawing } from "../../models/map/map-drawing";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RectDrawingWidget implements DrawingShapeWidget {
|
export class RectDrawingWidget implements DrawingShapeWidget {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private qtDasharrayFixer: QtDasharrayFixer
|
private qtDasharrayFixer: QtDasharrayFixer
|
||||||
) {}
|
) {}
|
||||||
@ -20,6 +21,22 @@ export class RectDrawingWidget implements DrawingShapeWidget {
|
|||||||
return (d.element && d.element instanceof RectElement) ? [d.element] : [];
|
return (d.element && d.element instanceof RectElement) ? [d.element] : [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
drawing.enter()
|
||||||
|
.append<SVGAElement>('line')
|
||||||
|
.attr("class", "top");
|
||||||
|
|
||||||
|
drawing.enter()
|
||||||
|
.append<SVGAElement>('line')
|
||||||
|
.attr("class", "bottom");
|
||||||
|
|
||||||
|
drawing.enter()
|
||||||
|
.append<SVGAElement>('line')
|
||||||
|
.attr("class", "right");
|
||||||
|
|
||||||
|
drawing.enter()
|
||||||
|
.append<SVGAElement>('line')
|
||||||
|
.attr("class", "left");
|
||||||
|
|
||||||
const drawing_enter = drawing
|
const drawing_enter = drawing
|
||||||
.enter()
|
.enter()
|
||||||
.append<SVGRectElement>('rect')
|
.append<SVGRectElement>('rect')
|
||||||
|
@ -75,6 +75,7 @@ export class TextDrawingWidget implements DrawingShapeWidget {
|
|||||||
// approx and make it matching to GUI
|
// approx and make it matching to GUI
|
||||||
const tspan = select(this).selectAll<SVGTSpanElement, string>('tspan');
|
const tspan = select(this).selectAll<SVGTSpanElement, string>('tspan');
|
||||||
const height = this.getBBox().height / tspan.size();
|
const height = this.getBBox().height / tspan.size();
|
||||||
|
//return `translate(0, ${height})`;
|
||||||
return `translate(${TextDrawingWidget.MARGIN}, ${height - TextDrawingWidget.MARGIN})`;
|
return `translate(${TextDrawingWidget.MARGIN}, ${height - TextDrawingWidget.MARGIN})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ 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()
|
||||||
@ -17,6 +18,7 @@ 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
|
||||||
) {
|
) {
|
||||||
@ -67,6 +69,7 @@ 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) {
|
||||||
|
@ -18,6 +18,84 @@ 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 {
|
||||||
|
position: fixed;
|
||||||
|
background: transparent;
|
||||||
|
top: 20px;
|
||||||
|
left: 92px;
|
||||||
|
width: 296px !important;
|
||||||
|
height: 72px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draw-menu button {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-drawer-content {
|
||||||
|
display: inline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-container {
|
||||||
|
height: 72px !important;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow {
|
||||||
|
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 {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
stroke: #0097a7!important;
|
||||||
|
}
|
||||||
|
|
||||||
.project-toolbar .mat-toolbar-multiple-rows {
|
.project-toolbar .mat-toolbar-multiple-rows {
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,13 @@
|
|||||||
[show-interface-labels]="project.show_interface_labels"
|
[show-interface-labels]="project.show_interface_labels"
|
||||||
[selection-tool]="tools.selection"
|
[selection-tool]="tools.selection"
|
||||||
[moving-tool]="tools.moving"
|
[moving-tool]="tools.moving"
|
||||||
|
[text-editing-tool]="tools.text_editing"
|
||||||
[draw-link-tool]="tools.draw_link"
|
[draw-link-tool]="tools.draw_link"
|
||||||
[readonly]="inReadOnlyMode"
|
[readonly]="inReadOnlyMode"
|
||||||
|
(nodeDragged)="onNodeDragged($event)"
|
||||||
|
(drawingDragged)="onDrawingDragged($event)"
|
||||||
|
(onLinkCreated)="onLinkCreated($event)"
|
||||||
|
(onDrawingResized)="onDrawingResized($event)"
|
||||||
></app-d3-map>
|
></app-d3-map>
|
||||||
|
|
||||||
<app-experimental-map
|
<app-experimental-map
|
||||||
@ -94,6 +99,41 @@
|
|||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="draw-menu">
|
||||||
|
<mat-drawer-container [ngClass]="{shadow: drawTools.visibility}" class="drawer-container">
|
||||||
|
<mat-drawer #drawer class="drawer">
|
||||||
|
<div class="drawer-buttons">
|
||||||
|
<button matTooltip="Add a note" mat-icon-button class="drawer-button" [color]="drawTools.isAddingTextChosen ? 'primary': 'basic'" (click)="addText()">
|
||||||
|
<mat-icon>create</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button matTooltip="Draw a rectangle" mat-icon-button class="drawer-button" [color]="drawTools.isRectangleChosen ? 'primary': 'basic'" (click)="addDrawing('rectangle')">
|
||||||
|
<mat-icon>crop_3_2</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button matTooltip="Draw an ellipse" mat-icon-button class="drawer-button" [color]="drawTools.isEllipseChosen ? 'primary': 'basic'" (click)="addDrawing('ellipse')">
|
||||||
|
<mat-icon>panorama_fish_eye</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button matTooltip="Draw line" mat-icon-button class="drawer-button" (click)="addDrawing('line')">
|
||||||
|
<svg height="40" width="40">
|
||||||
|
<line [ngClass]="{selected: drawTools.isLineChosen}" x1="30" y1="10" x2="10" y2="30" style="stroke:white;stroke-width:2" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div class="drawer-arrow-button-left">
|
||||||
|
<button mat-icon-button (click)="drawer.toggle(); hideMenu()">
|
||||||
|
<mat-icon>keyboard_arrow_left</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
|
||||||
<app-node-context-menu [project]="project" [server]="server"></app-node-context-menu>
|
<app-node-context-menu [project]="project" [server]="server"></app-node-context-menu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,14 +1,135 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ProjectMapComponent } from './project-map.component';
|
import { ProjectMapComponent } from './project-map.component';
|
||||||
|
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { ServerService } from '../../services/server.service';
|
||||||
|
import { ProjectService } from '../../services/project.service';
|
||||||
|
import { SettingsService } from '../../services/settings.service';
|
||||||
|
import { NodeService } from '../../services/node.service';
|
||||||
|
import { LinkService } from '../../services/link.service';
|
||||||
|
import { DrawingService } from '../../services/drawing.service';
|
||||||
|
import { ProgressService } from '../../common/progress/progress.service';
|
||||||
|
import { ProjectWebServiceHandler } from '../../handlers/project-web-service-handler';
|
||||||
|
import { MapChangeDetectorRef } from '../../cartography/services/map-change-detector-ref';
|
||||||
|
import { NodeWidget } from '../../cartography/widgets/node';
|
||||||
|
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 { 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 { CommonModule } from '@angular/common';
|
||||||
|
import { ANGULAR_MAP_DECLARATIONS } from '../../cartography/angular-map.imports';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { SymbolService } from '../../services/symbol.service';
|
||||||
|
import { MockedSettingsService } from '../../services/settings.service.spec';
|
||||||
|
import { MockedServerService } from '../../services/server.service.spec';
|
||||||
|
import { MockedProjectService } from '../../services/project.service.spec';
|
||||||
|
import { Observable } from 'rxjs/Rx';
|
||||||
|
import { Drawing } from '../../cartography/models/drawing';
|
||||||
|
import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component';
|
||||||
|
import { Project } from '../../models/project';
|
||||||
|
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 { ResizedDataEvent } from '../../cartography/events/event-source';
|
||||||
|
import { MapLabelToLabelConverter } from '../../cartography/converters/map/map-label-to-label-converter';
|
||||||
|
|
||||||
|
export class MockedProgressService {
|
||||||
|
public activate() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MockedDrawingService {
|
||||||
|
public drawing = {} as Drawing;
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
add(_server: Server, _project_id:string, _x: number, _y:number, _svg: string) {
|
||||||
|
return of(this.drawing);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePosition(_server: Server, _drawing: Drawing, _x: number, _y: number){
|
||||||
|
return of(this.drawing);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSizeAndPosition(_server: Server, _drawing: Drawing, _x: number, _y: number, _svg: string){
|
||||||
|
return of(this.drawing);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(_server: Server, _drawing: Drawing){
|
||||||
|
return of(this.drawing);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(_server: Server, _drawing: Drawing){
|
||||||
|
return of(this.drawing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MockedDrawingsDataSource {
|
||||||
|
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>;
|
||||||
|
let drawingService = new MockedDrawingService;
|
||||||
|
let drawingsDataSource = new MockedDrawingsDataSource;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ ProjectMapComponent ],
|
imports: [
|
||||||
|
MatIconModule,
|
||||||
|
MatToolbarModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
CommonModule,
|
||||||
|
NoopAnimationsModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: ActivatedRoute },
|
||||||
|
{ provide: ServerService, useClass: MockedServerService },
|
||||||
|
{ provide: ProjectService, useClass: MockedProjectService },
|
||||||
|
{ provide: SymbolService },
|
||||||
|
{ provide: NodeService },
|
||||||
|
{ provide: LinkService },
|
||||||
|
{ provide: DrawingService, useValue: drawingService},
|
||||||
|
{ provide: ProgressService, useClass: MockedProgressService },
|
||||||
|
{ provide: ProjectWebServiceHandler },
|
||||||
|
{ provide: MapChangeDetectorRef },
|
||||||
|
{ provide: NodeWidget },
|
||||||
|
{ provide: MapNodeToNodeConverter },
|
||||||
|
{ provide: MapPortToPortConverter },
|
||||||
|
{ provide: NodesDataSource },
|
||||||
|
{ provide: LinksDataSource },
|
||||||
|
{ provide: DrawingsDataSource, useValue: drawingsDataSource},
|
||||||
|
{ provide: NodesEventSource },
|
||||||
|
{ provide: DrawingsEventSource },
|
||||||
|
{ provide: LinksEventSource },
|
||||||
|
{ provide: MapDrawingToSvgConverter, useValue: {
|
||||||
|
convert: () => { return ''}
|
||||||
|
} },
|
||||||
|
{ provide: SettingsService, useClass: MockedSettingsService },
|
||||||
|
{ provide: MapLabelToLabelConverter}
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ProjectMapComponent,
|
||||||
|
D3MapComponent,
|
||||||
|
...ANGULAR_MAP_DECLARATIONS
|
||||||
|
],
|
||||||
|
schemas: [
|
||||||
|
NO_ERRORS_SCHEMA
|
||||||
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
@ -16,7 +137,66 @@ describe('ProjectMapComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ProjectMapComponent);
|
fixture = TestBed.createComponent(ProjectMapComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
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', () => {
|
||||||
|
var dummyElement = document.createElement('map');
|
||||||
|
document.getElementsByClassName = jasmine.createSpy('HTML element').and.callFake(() => { return ([dummyElement])});
|
||||||
|
spyOn(component, 'resetDrawToolChoice');
|
||||||
|
|
||||||
|
component.hideMenu();
|
||||||
|
|
||||||
|
expect(component.resetDrawToolChoice).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return drawing mock of correct type'), () => {
|
||||||
|
let mock = component.getDrawingMock('rectangle');
|
||||||
|
|
||||||
|
expect(mock instanceof RectElement).toBe(true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,7 @@ import { MapChangeDetectorRef } from '../../cartography/services/map-change-dete
|
|||||||
import { NodeContextMenu } from '../../cartography/events/nodes';
|
import { NodeContextMenu } from '../../cartography/events/nodes';
|
||||||
import { MapLinkCreated } from '../../cartography/events/links';
|
import { MapLinkCreated } from '../../cartography/events/links';
|
||||||
import { NodeWidget } from '../../cartography/widgets/node';
|
import { NodeWidget } from '../../cartography/widgets/node';
|
||||||
import { DraggedDataEvent } from '../../cartography/events/event-source';
|
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 { NodesEventSource } from '../../cartography/events/nodes-event-source';
|
||||||
@ -35,10 +35,18 @@ import { MapNode } from '../../cartography/models/map/map-node';
|
|||||||
import { LinksEventSource } from '../../cartography/events/links-event-source';
|
import { LinksEventSource } from '../../cartography/events/links-event-source';
|
||||||
import { MapDrawing } from '../../cartography/models/map/map-drawing';
|
import { MapDrawing } from '../../cartography/models/map/map-drawing';
|
||||||
import { MapPortToPortConverter } from '../../cartography/converters/map/map-port-to-port-converter';
|
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 { MapLabel } from '../../cartography/models/map/map-label';
|
||||||
|
import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component';
|
||||||
import { MapLinkNode } from '../../cartography/models/map/map-link-node';
|
import { MapLinkNode } from '../../cartography/models/map/map-link-node';
|
||||||
|
import { TextElement } from '../../cartography/models/drawings/text-element';
|
||||||
import { MapLabelToLabelConverter } from '../../cartography/converters/map/map-label-to-label-converter';
|
import { MapLabelToLabelConverter } from '../../cartography/converters/map/map-label-to-label-converter';
|
||||||
|
import { select } from 'd3-selection';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -54,20 +62,32 @@ 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;
|
||||||
private ws: Subject<any>;
|
private ws: Subject<any>;
|
||||||
|
|
||||||
tools = {
|
tools = {
|
||||||
'selection': true,
|
'selection': true,
|
||||||
'moving': false,
|
'moving': false,
|
||||||
'draw_link': false
|
'draw_link': false,
|
||||||
|
'text_editing': true
|
||||||
};
|
};
|
||||||
|
|
||||||
protected settings: Settings;
|
protected settings: Settings;
|
||||||
|
|
||||||
|
protected drawTools = {
|
||||||
|
'isRectangleChosen': false,
|
||||||
|
'isEllipseChosen': false,
|
||||||
|
'isLineChosen': false,
|
||||||
|
'visibility': false,
|
||||||
|
'isAddingTextChosen': false
|
||||||
|
};
|
||||||
|
|
||||||
|
protected selectedDrawing: string;
|
||||||
|
|
||||||
private inReadOnlyMode = false;
|
private inReadOnlyMode = false;
|
||||||
|
|
||||||
@ViewChild(NodeContextMenuComponent) nodeContextMenu: NodeContextMenuComponent;
|
@ViewChild(NodeContextMenuComponent) nodeContextMenu: NodeContextMenuComponent;
|
||||||
|
@ViewChild(D3MapComponent) mapChild: D3MapComponent;
|
||||||
|
|
||||||
private subscriptions: Subscription[] = [];
|
private subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
@ -77,7 +97,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
private projectService: ProjectService,
|
private projectService: ProjectService,
|
||||||
private nodeService: NodeService,
|
private nodeService: NodeService,
|
||||||
private linkService: LinkService,
|
private linkService: LinkService,
|
||||||
private drawingService: DrawingService,
|
public drawingService: DrawingService,
|
||||||
private progressService: ProgressService,
|
private progressService: ProgressService,
|
||||||
private projectWebServiceHandler: ProjectWebServiceHandler,
|
private projectWebServiceHandler: ProjectWebServiceHandler,
|
||||||
private mapChangeDetectorRef: MapChangeDetectorRef,
|
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||||
@ -90,6 +110,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
private nodesEventSource: NodesEventSource,
|
private nodesEventSource: NodesEventSource,
|
||||||
private drawingsEventSource: DrawingsEventSource,
|
private drawingsEventSource: DrawingsEventSource,
|
||||||
private linksEventSource: LinksEventSource,
|
private linksEventSource: LinksEventSource,
|
||||||
|
private mapDrawingToSvgConverter: MapDrawingToSvgConverter,
|
||||||
private settingsService: SettingsService,
|
private settingsService: SettingsService,
|
||||||
private mapLabelToLabel: MapLabelToLabelConverter
|
private mapLabelToLabel: MapLabelToLabelConverter
|
||||||
) {}
|
) {}
|
||||||
@ -169,6 +190,14 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
this.drawingsEventSource.dragged.subscribe((evt) => this.onDrawingDragged(evt))
|
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.subscriptions.push(
|
||||||
this.linksEventSource.created.subscribe((evt) => this.onLinkCreated(evt))
|
this.linksEventSource.created.subscribe((evt) => this.onLinkCreated(evt))
|
||||||
);
|
);
|
||||||
@ -312,6 +341,32 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
@ -340,6 +395,194 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
this.project.show_interface_labels = enabled;
|
this.project.show_interface_labels = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
case "rectangle":
|
||||||
|
this.drawTools.isAddingTextChosen = false;
|
||||||
|
this.drawTools.isEllipseChosen = false;
|
||||||
|
this.drawTools.isRectangleChosen = !this.drawTools.isRectangleChosen;
|
||||||
|
this.drawTools.isLineChosen = false;
|
||||||
|
break;
|
||||||
|
case "ellipse":
|
||||||
|
this.drawTools.isAddingTextChosen = false;
|
||||||
|
this.drawTools.isEllipseChosen = !this.drawTools.isEllipseChosen;
|
||||||
|
this.drawTools.isRectangleChosen = false;
|
||||||
|
this.drawTools.isLineChosen = false;
|
||||||
|
break;
|
||||||
|
case "line":
|
||||||
|
this.drawTools.isAddingTextChosen = false;
|
||||||
|
this.drawTools.isEllipseChosen = false;
|
||||||
|
this.drawTools.isRectangleChosen = false;
|
||||||
|
this.drawTools.isLineChosen = !this.drawTools.isLineChosen;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedDrawing = 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(){
|
||||||
|
this.drawTools.isRectangleChosen = false;
|
||||||
|
this.drawTools.isEllipseChosen = false;
|
||||||
|
this.drawTools.isLineChosen = false;
|
||||||
|
this.drawTools.isAddingTextChosen = false;
|
||||||
|
this.selectedDrawing = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public hideMenu(){
|
||||||
|
var map = document.getElementsByClassName('map')[0];
|
||||||
|
map.removeEventListener('click', this.drawListener as EventListenerOrEventListenerObject);
|
||||||
|
this.resetDrawToolChoice();
|
||||||
|
this.drawTools.visibility = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public showMenu(){
|
||||||
|
setTimeout(() => {
|
||||||
|
this.drawTools.visibility = true;
|
||||||
|
},
|
||||||
|
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();
|
||||||
|
@ -58,6 +58,24 @@ describe('DrawingService', () => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should update size and position of drawing', inject([DrawingService], (service: DrawingService) => {
|
||||||
|
const drawing = new Drawing();
|
||||||
|
drawing.project_id = "myproject";
|
||||||
|
drawing.drawing_id = "id";
|
||||||
|
let svgSample = "<svg><test></svg>"
|
||||||
|
|
||||||
|
service.updateSizeAndPosition(server, drawing, 100, 100, svgSample).subscribe();
|
||||||
|
|
||||||
|
const req = httpTestingController.expectOne(
|
||||||
|
'http://127.0.0.1:3080/v2/projects/myproject/drawings/id');
|
||||||
|
expect(req.request.method).toEqual("PUT");
|
||||||
|
expect(req.request.body).toEqual({
|
||||||
|
'x': 100,
|
||||||
|
'y': 100,
|
||||||
|
'svg': svgSample
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should update drawing', inject([DrawingService], (service: DrawingService) => {
|
it('should update drawing', inject([DrawingService], (service: DrawingService) => {
|
||||||
const drawing = new Drawing();
|
const drawing = new Drawing();
|
||||||
drawing.project_id = "myproject";
|
drawing.project_id = "myproject";
|
||||||
|
@ -12,6 +12,15 @@ export class DrawingService {
|
|||||||
|
|
||||||
constructor(private httpServer: HttpServer) { }
|
constructor(private httpServer: HttpServer) { }
|
||||||
|
|
||||||
|
add(server: Server, project_id:string, x: number, y:number, svg: string) {
|
||||||
|
return this.httpServer
|
||||||
|
.post<Drawing>(server, `/projects/${project_id}/drawings`, {
|
||||||
|
'svg': svg,
|
||||||
|
'x': x,
|
||||||
|
'y': y
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
updatePosition(server: Server, drawing: Drawing, x: number, y: number): Observable<Drawing> {
|
updatePosition(server: Server, drawing: Drawing, x: number, y: number): Observable<Drawing> {
|
||||||
return this.httpServer
|
return this.httpServer
|
||||||
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
||||||
@ -20,6 +29,25 @@ export class DrawingService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSizeAndPosition(server: Server, drawing: Drawing, x: number, y: number, svg: string): Observable<Drawing> {
|
||||||
|
return this.httpServer
|
||||||
|
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
||||||
|
'svg': svg,
|
||||||
|
'x': x,
|
||||||
|
'y': y
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateText(server: Server, drawing: Drawing, svg: string): Observable<Drawing> {
|
||||||
|
return this.httpServer
|
||||||
|
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
||||||
|
'svg': svg,
|
||||||
|
'x': drawing.x,
|
||||||
|
'y': drawing.y,
|
||||||
|
'z': drawing.z
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
update(server: Server, drawing: Drawing): Observable<Drawing> {
|
update(server: Server, drawing: Drawing): Observable<Drawing> {
|
||||||
return this.httpServer
|
return this.httpServer
|
||||||
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
||||||
|
@ -3118,10 +3118,10 @@ electron-to-chromium@^1.3.86:
|
|||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.88.tgz#f36ab32634f49ef2b0fdc1e82e2d1cc17feb29e7"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.88.tgz#f36ab32634f49ef2b0fdc1e82e2d1cc17feb29e7"
|
||||||
integrity sha512-UPV4NuQMKeUh1S0OWRvwg0PI8ASHN9kBC8yDTk1ROXLC85W5GnhTRu/MZu3Teqx3JjlQYuckuHYXSUSgtb3J+A==
|
integrity sha512-UPV4NuQMKeUh1S0OWRvwg0PI8ASHN9kBC8yDTk1ROXLC85W5GnhTRu/MZu3Teqx3JjlQYuckuHYXSUSgtb3J+A==
|
||||||
|
|
||||||
electron@3.0.10:
|
electron@3.0.11:
|
||||||
version "3.0.10"
|
version "3.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/electron/-/electron-3.0.10.tgz#7d412856e8cf0d3041a612a32dd09e2af2d50f50"
|
resolved "https://registry.yarnpkg.com/electron/-/electron-3.0.11.tgz#81e350db741fc0f2997ecb2fef5ed085ca42a723"
|
||||||
integrity sha512-I39IeQP3NOlbjKzTDK8uK2JdiHDfhV5SruCS2Gttkn2MaKCY+yIzQ6Wr4DyBXLeTEkL1sbZxbqQVhCavAliv5w==
|
integrity sha512-galllxAMT3HLbHNR6i5WXrUXzsxzz0D1X6vu3uFMhofU9Wdbxv2w7BAD/BcYTT4c1bu4nZEgXO6AvHXjq0Sksw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "^8.0.24"
|
"@types/node" "^8.0.24"
|
||||||
electron-download "^4.1.0"
|
electron-download "^4.1.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user