mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-06-13 12:38:09 +00:00
Drawings can communicate with server after dragged
This commit is contained in:
@ -67,6 +67,7 @@ import { CreateSnapshotDialogComponent } from './components/snapshots/create-sna
|
|||||||
import { SnapshotsComponent } from './components/snapshots/snapshots.component';
|
import { SnapshotsComponent } from './components/snapshots/snapshots.component';
|
||||||
import { SnapshotMenuItemComponent } from './components/snapshots/snapshot-menu-item/snapshot-menu-item.component';
|
import { SnapshotMenuItemComponent } from './components/snapshots/snapshot-menu-item/snapshot-menu-item.component';
|
||||||
import { MATERIAL_IMPORTS } from './material.imports';
|
import { MATERIAL_IMPORTS } from './material.imports';
|
||||||
|
import { DrawingService } from './services/drawing.service';
|
||||||
|
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
@ -131,6 +132,7 @@ if (environment.production) {
|
|||||||
ApplianceService,
|
ApplianceService,
|
||||||
NodeService,
|
NodeService,
|
||||||
LinkService,
|
LinkService,
|
||||||
|
DrawingService,
|
||||||
IndexedDbService,
|
IndexedDbService,
|
||||||
HttpServer,
|
HttpServer,
|
||||||
SnapshotService,
|
SnapshotService,
|
||||||
|
@ -16,7 +16,11 @@ import { MapChangeDetectorRef } from './services/map-change-detector-ref';
|
|||||||
import { Context } from './models/context';
|
import { Context } from './models/context';
|
||||||
import { D3_MAP_IMPORTS } from './d3-map.imports';
|
import { D3_MAP_IMPORTS } from './d3-map.imports';
|
||||||
import { CanvasSizeDetector } from './helpers/canvas-size-detector';
|
import { CanvasSizeDetector } from './helpers/canvas-size-detector';
|
||||||
import { MapListener } from './listeners/map-listener';
|
import { MapListeners } from './listeners/map-listeners';
|
||||||
|
import { DrawingsDraggableListener } from './listeners/drawings-draggable-listener';
|
||||||
|
import { NodesDraggableListener } from './listeners/nodes-draggable-listener';
|
||||||
|
import { DrawingsEventSource } from './events/drawings-event-source';
|
||||||
|
import { NodesEventSource } from './events/nodes-event-source';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -40,7 +44,11 @@ import { MapListener } from './listeners/map-listener';
|
|||||||
MapChangeDetectorRef,
|
MapChangeDetectorRef,
|
||||||
CanvasSizeDetector,
|
CanvasSizeDetector,
|
||||||
Context,
|
Context,
|
||||||
MapListener,
|
MapListeners,
|
||||||
|
DrawingsDraggableListener,
|
||||||
|
NodesDraggableListener,
|
||||||
|
DrawingsEventSource,
|
||||||
|
NodesEventSource,
|
||||||
...D3_MAP_IMPORTS
|
...D3_MAP_IMPORTS
|
||||||
],
|
],
|
||||||
exports: [ MapComponent ]
|
exports: [ MapComponent ]
|
||||||
|
@ -20,9 +20,11 @@ import { MapChangeDetectorRef } from '../../services/map-change-detector-ref';
|
|||||||
import { NodeDragging, NodeDragged, NodeClicked } from '../../events/nodes';
|
import { NodeDragging, NodeDragged, NodeClicked } from '../../events/nodes';
|
||||||
import { LinkCreated } from '../../events/links';
|
import { LinkCreated } from '../../events/links';
|
||||||
import { CanvasSizeDetector } from '../../helpers/canvas-size-detector';
|
import { CanvasSizeDetector } from '../../helpers/canvas-size-detector';
|
||||||
import { SelectionManager } from '../../managers/selection-manager';
|
|
||||||
import { NodeWidget } from '../../widgets/node';
|
import { NodeWidget } from '../../widgets/node';
|
||||||
import { MapListener } from '../../listeners/map-listener';
|
import { MapListeners } from '../../listeners/map-listeners';
|
||||||
|
import { DraggedDataEvent } from '../../events/event-source';
|
||||||
|
import { NodesEventSource } from '../../events/nodes-event-source';
|
||||||
|
import { DrawingsEventSource } from '../../events/drawings-event-source';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -39,16 +41,13 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
@Input() width = 1500;
|
@Input() width = 1500;
|
||||||
@Input() height = 600;
|
@Input() height = 600;
|
||||||
|
|
||||||
@Output() onNodeDragged = new EventEmitter<NodeDragged>();
|
@Output() nodeDragged: EventEmitter<DraggedDataEvent<Node>>;
|
||||||
|
@Output() drawingDragged: EventEmitter<DraggedDataEvent<Drawing>>;
|
||||||
@Output() onLinkCreated = new EventEmitter<LinkCreated>();
|
@Output() onLinkCreated = new EventEmitter<LinkCreated>();
|
||||||
|
|
||||||
private parentNativeElement: any;
|
private parentNativeElement: any;
|
||||||
private svg: Selection<SVGSVGElement, any, null, undefined>;
|
private svg: Selection<SVGSVGElement, any, null, undefined>;
|
||||||
|
|
||||||
private onNodeDraggingSubscription: Subscription;
|
|
||||||
private onNodeClickedSubscription: Subscription;
|
|
||||||
private onNodeDraggedSubscription: Subscription;
|
|
||||||
|
|
||||||
private onChangesDetected: Subscription;
|
private onChangesDetected: Subscription;
|
||||||
|
|
||||||
protected settings = {
|
protected settings = {
|
||||||
@ -59,8 +58,7 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
private context: Context,
|
private context: Context,
|
||||||
private mapChangeDetectorRef: MapChangeDetectorRef,
|
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||||
private canvasSizeDetector: CanvasSizeDetector,
|
private canvasSizeDetector: CanvasSizeDetector,
|
||||||
private mapListener: MapListener,
|
private mapListeners: MapListeners,
|
||||||
private selectionManager: SelectionManager,
|
|
||||||
protected element: ElementRef,
|
protected element: ElementRef,
|
||||||
protected nodesWidget: NodesWidget,
|
protected nodesWidget: NodesWidget,
|
||||||
protected nodeWidget: NodeWidget,
|
protected nodeWidget: NodeWidget,
|
||||||
@ -68,9 +66,13 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
protected interfaceLabelWidget: InterfaceLabelWidget,
|
protected interfaceLabelWidget: InterfaceLabelWidget,
|
||||||
protected selectionToolWidget: SelectionTool,
|
protected selectionToolWidget: SelectionTool,
|
||||||
protected movingToolWidget: MovingTool,
|
protected movingToolWidget: MovingTool,
|
||||||
public graphLayout: GraphLayout
|
public graphLayout: GraphLayout,
|
||||||
|
nodesEventSource: NodesEventSource,
|
||||||
|
drawingsEventSource: DrawingsEventSource,
|
||||||
) {
|
) {
|
||||||
this.parentNativeElement = element.nativeElement;
|
this.parentNativeElement = element.nativeElement;
|
||||||
|
this.nodeDragged = nodesEventSource.dragged;
|
||||||
|
this.drawingDragged = drawingsEventSource.dragged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input('show-interface-labels')
|
@Input('show-interface-labels')
|
||||||
@ -118,67 +120,25 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.graphLayout.disconnect(this.svg);
|
|
||||||
this.onNodeDraggingSubscription.unsubscribe();
|
|
||||||
this.onNodeClickedSubscription.unsubscribe();
|
|
||||||
this.onNodeDraggedSubscription.unsubscribe();
|
|
||||||
this.onChangesDetected.unsubscribe();
|
|
||||||
this.mapListener.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.parentNativeElement !== null) {
|
if (this.parentNativeElement !== null) {
|
||||||
this.createGraph(this.parentNativeElement);
|
this.createGraph(this.parentNativeElement);
|
||||||
}
|
}
|
||||||
this.context.size = this.getSize();
|
this.context.size = this.getSize();
|
||||||
|
|
||||||
this.onNodeDraggingSubscription = this.nodeWidget.onNodeDragging.subscribe((eventNode: NodeDragging) => {
|
|
||||||
let nodes = this.selectionManager.getSelectedNodes();
|
|
||||||
|
|
||||||
if (nodes.filter((n: Node) => n.node_id === eventNode.node.node_id).length === 0) {
|
|
||||||
this.selectionManager.setSelectedNodes([eventNode.node]);
|
|
||||||
nodes = this.selectionManager.getSelectedNodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.forEach((node: Node) => {
|
|
||||||
node.x += eventNode.event.dx;
|
|
||||||
node.y += eventNode.event.dy;
|
|
||||||
|
|
||||||
this.nodesWidget.redrawNode(this.svg, node);
|
|
||||||
const links = this.links.filter((link) => link.target.node_id === node.node_id || link.source.node_id === node.node_id);
|
|
||||||
links.forEach((link) => {
|
|
||||||
this.linksWidget.redrawLink(this.svg, link);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
this.onNodeDraggedSubscription = this.nodeWidget.onNodeDragged.subscribe((eventNode: NodeDragged) => {
|
|
||||||
let nodes = this.selectionManager.getSelectedNodes();
|
|
||||||
|
|
||||||
if (nodes.filter((n: Node) => n.node_id === eventNode.node.node_id).length === 0) {
|
|
||||||
this.selectionManager.setSelectedNodes([eventNode.node]);
|
|
||||||
nodes = this.selectionManager.getSelectedNodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.forEach((node) => {
|
|
||||||
this.onNodeDragged.emit(new NodeDragged(eventNode.event, node));
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
this.onNodeClickedSubscription = this.nodeWidget.onNodeClicked.subscribe((nodeClickedEvent: NodeClicked) => {
|
|
||||||
this.selectionManager.setSelectedNodes([nodeClickedEvent.node]);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.onChangesDetected = this.mapChangeDetectorRef.changesDetected.subscribe(() => {
|
this.onChangesDetected = this.mapChangeDetectorRef.changesDetected.subscribe(() => {
|
||||||
if (this.mapChangeDetectorRef.hasBeenDrawn) {
|
if (this.mapChangeDetectorRef.hasBeenDrawn) {
|
||||||
this.reload();
|
this.reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.mapListener.onInit(this.svg);
|
this.mapListeners.onInit(this.svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.graphLayout.disconnect(this.svg);
|
||||||
|
this.onChangesDetected.unsubscribe();
|
||||||
|
this.mapListeners.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
public createGraph(domElement: HTMLElement) {
|
public createGraph(domElement: HTMLElement) {
|
||||||
|
@ -3,6 +3,8 @@ import { drag, DraggedElementBaseType } from "d3-drag";
|
|||||||
import { event } from "d3-selection";
|
import { event } from "d3-selection";
|
||||||
|
|
||||||
class DraggableEvent {
|
class DraggableEvent {
|
||||||
|
public x: number;
|
||||||
|
public y: number;
|
||||||
public dx: number;
|
public dx: number;
|
||||||
public dy: number;
|
public dy: number;
|
||||||
}
|
}
|
||||||
@ -46,18 +48,24 @@ export class Draggable<GElement extends DraggedElementBaseType, Datum> {
|
|||||||
const evt = new DraggableStart<Datum>(datum);
|
const evt = new DraggableStart<Datum>(datum);
|
||||||
evt.dx = event.dx;
|
evt.dx = event.dx;
|
||||||
evt.dy = event.dy;
|
evt.dy = event.dy;
|
||||||
|
evt.x = event.x;
|
||||||
|
evt.y = event.y;
|
||||||
this.start.emit(evt);
|
this.start.emit(evt);
|
||||||
})
|
})
|
||||||
.on('drag', (datum: Datum) => {
|
.on('drag', (datum: Datum) => {
|
||||||
const evt = new DraggableDrag<Datum>(datum);
|
const evt = new DraggableDrag<Datum>(datum);
|
||||||
evt.dx = event.dx;
|
evt.dx = event.dx;
|
||||||
evt.dy = event.dy;
|
evt.dy = event.dy;
|
||||||
|
evt.x = event.x;
|
||||||
|
evt.y = event.y;
|
||||||
this.drag.emit(evt);
|
this.drag.emit(evt);
|
||||||
})
|
})
|
||||||
.on('end', (datum: Datum) => {
|
.on('end', (datum: Datum) => {
|
||||||
const evt = new DraggableEnd<Datum>(datum);
|
const evt = new DraggableEnd<Datum>(datum);
|
||||||
evt.dx = event.dx;
|
evt.dx = event.dx;
|
||||||
evt.dy = event.dy;
|
evt.dy = event.dy;
|
||||||
|
evt.x = event.x;
|
||||||
|
evt.y = event.y;
|
||||||
this.end.emit(evt);
|
this.end.emit(evt);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
9
src/app/cartography/events/drawings-event-source.ts
Normal file
9
src/app/cartography/events/drawings-event-source.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Injectable, EventEmitter } from "@angular/core";
|
||||||
|
import { Drawing } from "../models/drawing";
|
||||||
|
import { DraggedDataEvent } from "./event-source";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DrawingsEventSource {
|
||||||
|
public dragged = new EventEmitter<DraggedDataEvent<Drawing>>();
|
||||||
|
}
|
9
src/app/cartography/events/event-source.ts
Normal file
9
src/app/cartography/events/event-source.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export class DataEventSource<T> {
|
||||||
|
constructor(
|
||||||
|
public datum: T
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// class CreatedDataEvent<T> extends DataEventSource<T> {}
|
||||||
|
export class DraggedDataEvent<T> extends DataEventSource<T> {}
|
9
src/app/cartography/events/nodes-event-source.ts
Normal file
9
src/app/cartography/events/nodes-event-source.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Injectable, EventEmitter } from "@angular/core";
|
||||||
|
import { Node } from "../models/node";
|
||||||
|
import { DraggedDataEvent } from "./event-source";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class NodesEventSource {
|
||||||
|
public dragged = new EventEmitter<DraggedDataEvent<Node>>();
|
||||||
|
}
|
55
src/app/cartography/listeners/drawings-draggable-listener.ts
Normal file
55
src/app/cartography/listeners/drawings-draggable-listener.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { DrawingsWidget } from "../widgets/drawings";
|
||||||
|
import { DraggableStart } from "../events/draggable";
|
||||||
|
import { Drawing } from "../models/drawing";
|
||||||
|
import { Subscription } from "rxjs";
|
||||||
|
import { SelectionManager } from "../managers/selection-manager";
|
||||||
|
import { DrawingsEventSource } from "../events/drawings-event-source";
|
||||||
|
import { DraggedDataEvent } from "../events/event-source";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DrawingsDraggableListener {
|
||||||
|
private start: Subscription;
|
||||||
|
private drag: Subscription;
|
||||||
|
private end: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingsWidget: DrawingsWidget,
|
||||||
|
private selectionManager: SelectionManager,
|
||||||
|
private drawingsEventSource: DrawingsEventSource
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public onInit(svg: any) {
|
||||||
|
this.start = this.drawingsWidget.draggable.start.subscribe((evt: DraggableStart<Drawing>) => {
|
||||||
|
let drawings = this.selectionManager.getSelectedDrawings();
|
||||||
|
if (drawings.filter((n: Drawing) => n.drawing_id === evt.datum.drawing_id).length === 0) {
|
||||||
|
this.selectionManager.setSelectedDrawings([evt.datum]);
|
||||||
|
drawings = this.selectionManager.getSelectedDrawings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.drag = this.drawingsWidget.draggable.drag.subscribe((evt: DraggableStart<Drawing>) => {
|
||||||
|
let drawings = this.selectionManager.getSelectedDrawings();
|
||||||
|
drawings.forEach((drawing: Drawing) => {
|
||||||
|
drawing.x += evt.dx;
|
||||||
|
drawing.y += evt.dy;
|
||||||
|
this.drawingsWidget.redrawDrawing(svg, drawing);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.end = this.drawingsWidget.draggable.end.subscribe((evt: DraggableStart<Drawing>) => {
|
||||||
|
let drawings = this.selectionManager.getSelectedDrawings();
|
||||||
|
drawings.forEach((drawing: Drawing) => {
|
||||||
|
this.drawingsEventSource.dragged.emit(new DraggedDataEvent<Drawing>(drawing));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDestroy() {
|
||||||
|
this.start.unsubscribe();
|
||||||
|
this.drag.unsubscribe();
|
||||||
|
this.end.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -1,53 +1,4 @@
|
|||||||
import { Injectable } from "@angular/core";
|
export interface MapListener {
|
||||||
import { DrawingsWidget } from "../widgets/drawings";
|
onInit(svg: any);
|
||||||
import { DraggableStart } from "../events/draggable";
|
onDestroy();
|
||||||
import { Drawing } from "../models/drawing";
|
|
||||||
import { Subscription } from "rxjs";
|
|
||||||
import { SelectionManager } from "../managers/selection-manager";
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class MapListener {
|
|
||||||
private start: Subscription;
|
|
||||||
private drag: Subscription;
|
|
||||||
private end: Subscription;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private drawingsWidget: DrawingsWidget,
|
|
||||||
private selectionManager: SelectionManager
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public onInit(svg: any) {
|
|
||||||
this.start = this.drawingsWidget.draggable.start.subscribe((evt: DraggableStart<Drawing>) => {
|
|
||||||
let drawings = this.selectionManager.getSelectedDrawings();
|
|
||||||
|
|
||||||
if (drawings.filter((n: Drawing) => n.drawing_id === evt.datum.drawing_id).length === 0) {
|
|
||||||
this.selectionManager.setSelectedDrawings([evt.datum]);
|
|
||||||
drawings = this.selectionManager.getSelectedDrawings();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.drag = this.drawingsWidget.draggable.drag.subscribe((evt: DraggableStart<Drawing>) => {
|
|
||||||
let drawings = this.selectionManager.getSelectedDrawings();
|
|
||||||
drawings.forEach((drawing: Drawing) => {
|
|
||||||
drawing.x += evt.dx;
|
|
||||||
drawing.y += evt.dy;
|
|
||||||
this.drawingsWidget.redrawDrawing(svg, drawing);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.end = this.drawingsWidget.draggable.end.subscribe((evt: DraggableStart<Drawing>) => {
|
|
||||||
let drawings = this.selectionManager.getSelectedDrawings();
|
|
||||||
drawings.forEach((drawing: Drawing) => {
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public onDestroy() {
|
|
||||||
this.start.unsubscribe();
|
|
||||||
this.drag.unsubscribe();
|
|
||||||
this.end.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
}
|
29
src/app/cartography/listeners/map-listeners.ts
Normal file
29
src/app/cartography/listeners/map-listeners.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { MapListener } from "./map-listener";
|
||||||
|
import { DrawingsDraggableListener } from "./drawings-draggable-listener";
|
||||||
|
import { NodesDraggableListener } from "./nodes-draggable-listener";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MapListeners {
|
||||||
|
private listeners: MapListener[] = [];
|
||||||
|
constructor(
|
||||||
|
private drawingsDraggableListener: DrawingsDraggableListener,
|
||||||
|
private nodesDraggableListener: NodesDraggableListener
|
||||||
|
) {
|
||||||
|
this.listeners.push(this.drawingsDraggableListener);
|
||||||
|
this.listeners.push(this.nodesDraggableListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onInit(svg: any) {
|
||||||
|
this.listeners.forEach((listener) => {
|
||||||
|
listener.onInit(svg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDestroy() {
|
||||||
|
this.listeners.forEach((listener) => {
|
||||||
|
listener.onDestroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
64
src/app/cartography/listeners/nodes-draggable-listener.ts
Normal file
64
src/app/cartography/listeners/nodes-draggable-listener.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { NodesWidget } from "../widgets/nodes";
|
||||||
|
import { DraggableStart } from "../events/draggable";
|
||||||
|
import { Node } from "../models/node";
|
||||||
|
import { Subscription } from "rxjs";
|
||||||
|
import { SelectionManager } from "../managers/selection-manager";
|
||||||
|
import { LinksWidget } from "../widgets/links";
|
||||||
|
import { GraphLayout } from "../widgets/graph-layout";
|
||||||
|
import { NodesEventSource } from "../events/nodes-event-source";
|
||||||
|
import { DraggedDataEvent } from "../events/event-source";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class NodesDraggableListener {
|
||||||
|
private start: Subscription;
|
||||||
|
private drag: Subscription;
|
||||||
|
private end: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private nodesWidget: NodesWidget,
|
||||||
|
private linksWidget: LinksWidget,
|
||||||
|
private selectionManager: SelectionManager,
|
||||||
|
private graphLayout: GraphLayout,
|
||||||
|
private nodesEventSource: NodesEventSource
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public onInit(svg: any) {
|
||||||
|
this.start = this.nodesWidget.draggable.start.subscribe((evt: DraggableStart<Node>) => {
|
||||||
|
let nodes = this.selectionManager.getSelectedNodes();
|
||||||
|
if (nodes.filter((n: Node) => n.node_id === evt.datum.node_id).length === 0) {
|
||||||
|
this.selectionManager.setSelectedNodes([evt.datum]);
|
||||||
|
nodes = this.selectionManager.getSelectedNodes();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.drag = this.nodesWidget.draggable.drag.subscribe((evt: DraggableStart<Node>) => {
|
||||||
|
let nodes = this.selectionManager.getSelectedNodes();
|
||||||
|
nodes.forEach((node: Node) => {
|
||||||
|
node.x += evt.dx;
|
||||||
|
node.y += evt.dy;
|
||||||
|
this.nodesWidget.redrawNode(svg, node);
|
||||||
|
|
||||||
|
const links = this.graphLayout.getLinks().filter((link) => link.target.node_id === node.node_id || link.source.node_id === node.node_id);
|
||||||
|
links.forEach((link) => {
|
||||||
|
this.linksWidget.redrawLink(svg, link);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.end = this.nodesWidget.draggable.end.subscribe((evt: DraggableStart<Node>) => {
|
||||||
|
let nodes = this.selectionManager.getSelectedNodes();
|
||||||
|
nodes.forEach((node: Node) => {
|
||||||
|
this.nodesEventSource.dragged.emit(new DraggedDataEvent<Node>(node));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDestroy() {
|
||||||
|
this.start.unsubscribe();
|
||||||
|
this.drag.unsubscribe();
|
||||||
|
this.end.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -36,6 +36,10 @@ export class GraphLayout implements Widget {
|
|||||||
this.links = links;
|
this.links = links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getLinks() {
|
||||||
|
return this.links;
|
||||||
|
}
|
||||||
|
|
||||||
public setDrawings(drawings: Drawing[]) {
|
public setDrawings(drawings: Drawing[]) {
|
||||||
this.drawings = drawings;
|
this.drawings = drawings;
|
||||||
}
|
}
|
||||||
|
@ -118,23 +118,5 @@ export class NodeWidget implements Widget {
|
|||||||
}
|
}
|
||||||
return n.label.y + bbox.height - NodeWidget.NODE_LABEL_MARGIN;
|
return n.label.y + bbox.height - NodeWidget.NODE_LABEL_MARGIN;
|
||||||
});
|
});
|
||||||
|
|
||||||
const callback = function (this: SVGGElement, n: Node) {
|
|
||||||
const e: D3DragEvent<SVGGElement, Node, Node> = event;
|
|
||||||
self.onNodeDragging.emit(new NodeDragging(e, n));
|
|
||||||
};
|
|
||||||
|
|
||||||
const dragging = () => {
|
|
||||||
return drag<SVGGElement, Node>()
|
|
||||||
.on('drag', callback)
|
|
||||||
.on('end', (n: Node) => {
|
|
||||||
const e: D3DragEvent<SVGGElement, Node, Node> = event;
|
|
||||||
self.onNodeDragged.emit(new NodeDragged(e, n));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.draggingEnabled) {
|
|
||||||
node_body_merge.call(dragging());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,15 @@ import { Node } from "../models/node";
|
|||||||
import { SVGSelection } from "../models/types";
|
import { SVGSelection } from "../models/types";
|
||||||
import { Layer } from "../models/layer";
|
import { Layer } from "../models/layer";
|
||||||
import { NodeWidget } from "./node";
|
import { NodeWidget } from "./node";
|
||||||
|
import { Draggable } from "../events/draggable";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NodesWidget implements Widget {
|
export class NodesWidget implements Widget {
|
||||||
static NODE_LABEL_MARGIN = 3;
|
static NODE_LABEL_MARGIN = 3;
|
||||||
|
|
||||||
|
public draggable = new Draggable<SVGGElement, Node>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private nodeWidget: NodeWidget
|
private nodeWidget: NodeWidget
|
||||||
) {
|
) {
|
||||||
@ -44,6 +47,8 @@ export class NodesWidget implements Widget {
|
|||||||
node
|
node
|
||||||
.exit()
|
.exit()
|
||||||
.remove();
|
.remove();
|
||||||
|
|
||||||
|
this.draggable.call(merge);
|
||||||
}
|
}
|
||||||
|
|
||||||
private selectNode(view: SVGSelection, node: Node) {
|
private selectNode(view: SVGSelection, node: Node) {
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
[selection-tool]="tools.selection"
|
[selection-tool]="tools.selection"
|
||||||
[moving-tool]="tools.moving"
|
[moving-tool]="tools.moving"
|
||||||
[draw-link-tool]="tools.draw_link"
|
[draw-link-tool]="tools.draw_link"
|
||||||
(onNodeDragged)="onNodeDragged($event)"
|
(nodeDragged)="onNodeDragged($event)"
|
||||||
|
(drawingDragged)="onDrawingDragged($event)"
|
||||||
(onLinkCreated)="onLinkCreated($event)"
|
(onLinkCreated)="onLinkCreated($event)"
|
||||||
></app-map>
|
></app-map>
|
||||||
<div class="project-toolbar">
|
<div class="project-toolbar">
|
||||||
|
@ -30,6 +30,8 @@ import { MapChangeDetectorRef } from '../../cartography/services/map-change-dete
|
|||||||
import { NodeContextMenu, NodeDragged } from '../../cartography/events/nodes';
|
import { NodeContextMenu, NodeDragged } from '../../cartography/events/nodes';
|
||||||
import { LinkCreated } from '../../cartography/events/links';
|
import { LinkCreated } from '../../cartography/events/links';
|
||||||
import { NodeWidget } from '../../cartography/widgets/node';
|
import { NodeWidget } from '../../cartography/widgets/node';
|
||||||
|
import { DraggedDataEvent } from '../../cartography/events/event-source';
|
||||||
|
import { DrawingService } from '../../services/drawing.service';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -70,6 +72,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
private symbolService: SymbolService,
|
private symbolService: SymbolService,
|
||||||
private nodeService: NodeService,
|
private nodeService: NodeService,
|
||||||
private linkService: LinkService,
|
private linkService: LinkService,
|
||||||
|
private drawingService: DrawingService,
|
||||||
private progressService: ProgressService,
|
private progressService: ProgressService,
|
||||||
private projectWebServiceHandler: ProjectWebServiceHandler,
|
private projectWebServiceHandler: ProjectWebServiceHandler,
|
||||||
private mapChangeDetectorRef: MapChangeDetectorRef,
|
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||||
@ -216,12 +219,21 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodeDragged(nodeEvent: NodeDragged) {
|
onNodeDragged(draggedEvent: DraggedDataEvent<Node>) {
|
||||||
this.nodesDataSource.update(nodeEvent.node);
|
this.nodesDataSource.update(draggedEvent.datum);
|
||||||
this.nodeService
|
this.nodeService
|
||||||
.updatePosition(this.server, nodeEvent.node, nodeEvent.node.x, nodeEvent.node.y)
|
.updatePosition(this.server, draggedEvent.datum, draggedEvent.datum.x, draggedEvent.datum.y)
|
||||||
.subscribe((n: Node) => {
|
.subscribe((node: Node) => {
|
||||||
this.nodesDataSource.update(n);
|
this.nodesDataSource.update(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDrawingDragged(draggedEvent: DraggedDataEvent<Drawing>) {
|
||||||
|
this.drawingsDataSource.update(draggedEvent.datum);
|
||||||
|
this.drawingService
|
||||||
|
.updatePosition(this.server, draggedEvent.datum, draggedEvent.datum.x, draggedEvent.datum.y)
|
||||||
|
.subscribe((drawing: Drawing) => {
|
||||||
|
this.drawingsDataSource.update(drawing);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
95
src/app/services/drawing.service.spec.ts
Normal file
95
src/app/services/drawing.service.spec.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
|
import { HttpServer } from './http-server.service';
|
||||||
|
import { Server } from '../models/server';
|
||||||
|
import { Drawing } from '../cartography/models/drawing';
|
||||||
|
import { getTestServer } from './testing';
|
||||||
|
import { DrawingService } from './drawing.service';
|
||||||
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
|
|
||||||
|
describe('DrawingService', () => {
|
||||||
|
let httpClient: HttpClient;
|
||||||
|
let httpTestingController: HttpTestingController;
|
||||||
|
let httpServer: HttpServer;
|
||||||
|
let service: DrawingService;
|
||||||
|
let server: Server;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
HttpClientTestingModule,
|
||||||
|
AppTestingModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
HttpServer,
|
||||||
|
DrawingService
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
httpClient = TestBed.get(HttpClient);
|
||||||
|
httpTestingController = TestBed.get(HttpTestingController);
|
||||||
|
httpServer = TestBed.get(HttpServer);
|
||||||
|
service = TestBed.get(DrawingService);
|
||||||
|
server = getTestServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
httpTestingController.verify();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([DrawingService], (service: DrawingService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should updatePosition of drawing', inject([DrawingService], (service: DrawingService) => {
|
||||||
|
const drawing = new Drawing();
|
||||||
|
drawing.project_id = "myproject";
|
||||||
|
drawing.drawing_id = "id";
|
||||||
|
|
||||||
|
service.updatePosition(server, drawing, 10, 20).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': 10,
|
||||||
|
'y': 20
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should update drawing', inject([DrawingService], (service: DrawingService) => {
|
||||||
|
const drawing = new Drawing();
|
||||||
|
drawing.project_id = "myproject";
|
||||||
|
drawing.drawing_id = "id";
|
||||||
|
drawing.x = 10;
|
||||||
|
drawing.y = 20;
|
||||||
|
drawing.z = 30;
|
||||||
|
|
||||||
|
service.update(server, drawing).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': 10,
|
||||||
|
'y': 20,
|
||||||
|
'z': 30
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should delete drawing', inject([DrawingService], (service: DrawingService) => {
|
||||||
|
const drawing = new Drawing();
|
||||||
|
drawing.project_id = "myproject";
|
||||||
|
drawing.drawing_id = "id";
|
||||||
|
|
||||||
|
service.delete(server, drawing).subscribe();
|
||||||
|
|
||||||
|
const req = httpTestingController.expectOne(
|
||||||
|
'http://127.0.0.1:3080/v2/projects/myproject/drawings/id');
|
||||||
|
expect(req.request.method).toEqual("DELETE");
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
36
src/app/services/drawing.service.ts
Normal file
36
src/app/services/drawing.service.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Drawing } from '../cartography/models/drawing';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
import { Server } from "../models/server";
|
||||||
|
import { HttpServer } from "./http-server.service";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DrawingService {
|
||||||
|
|
||||||
|
constructor(private httpServer: HttpServer) { }
|
||||||
|
|
||||||
|
updatePosition(server: Server, drawing: Drawing, x: number, y: number): Observable<Drawing> {
|
||||||
|
return this.httpServer
|
||||||
|
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
||||||
|
'x': x,
|
||||||
|
'y': y
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update(server: Server, drawing: Drawing): Observable<Drawing> {
|
||||||
|
return this.httpServer
|
||||||
|
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
||||||
|
'x': drawing.x,
|
||||||
|
'y': drawing.y,
|
||||||
|
'z': drawing.z
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(server: Server, drawing: Drawing) {
|
||||||
|
return this.httpServer.delete<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user