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

This commit is contained in:
PiotrP 2018-11-26 07:17:39 -08:00
commit 6fc98e3577
24 changed files with 259 additions and 122 deletions

View File

@ -42,6 +42,7 @@ import { SelectionEventSource } from './events/selection-event-source';
import { SelectionControlComponent } from './components/selection-control/selection-control.component';
import { SelectionSelectComponent } from './components/selection-select/selection-select.component';
import { DraggableSelectionComponent } from './components/draggable-selection/draggable-selection.component';
import { MapSettingsManager } from './managers/map-settings-manager';
@NgModule({
@ -94,6 +95,7 @@ import { DraggableSelectionComponent } from './components/draggable-selection/dr
MapDrawingsDataSource,
MapSymbolsDataSource,
SelectionEventSource,
MapSettingsManager,
...D3_MAP_IMPORTS
],
exports: [ D3MapComponent, ExperimentalMapComponent ]

View File

@ -6,7 +6,6 @@ import { Selection, select } from 'd3-selection';
import { GraphLayout } from "../../widgets/graph-layout";
import { Context } from "../../models/context";
import { Size } from "../../models/size";
import { NodesWidget } from '../../widgets/nodes';
import { Subscription } from 'rxjs';
import { InterfaceLabelWidget } from '../../widgets/interface-label';
import { SelectionTool } from '../../tools/selection-tool';
@ -14,13 +13,13 @@ import { MovingTool } from '../../tools/moving-tool';
import { MapChangeDetectorRef } from '../../services/map-change-detector-ref';
import { MapLinkCreated } from '../../events/links';
import { CanvasSizeDetector } from '../../helpers/canvas-size-detector';
import { DrawingsWidget } from '../../widgets/drawings';
import { Node } from '../../models/node';
import { Link } from '../../../models/link';
import { Drawing } from '../../models/drawing';
import { Symbol } from '../../../models/symbol';
import { GraphDataManager } from '../../managers/graph-data-manager';
import { DraggedDataEvent } from '../../events/event-source';
import { MapSettingsManager } from '../../managers/map-settings-manager';
@Component({
@ -56,9 +55,8 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
public context: Context,
private mapChangeDetectorRef: MapChangeDetectorRef,
private canvasSizeDetector: CanvasSizeDetector,
private mapSettings: MapSettingsManager,
protected element: ElementRef,
protected nodesWidget: NodesWidget,
protected drawingsWidget: DrawingsWidget,
protected interfaceLabelWidget: InterfaceLabelWidget,
protected selectionToolWidget: SelectionTool,
protected movingToolWidget: MovingTool,
@ -92,8 +90,7 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
public drawingSelected = "";
@Input('readonly') set readonly(value) {
this.nodesWidget.draggingEnabled = !value;
this.drawingsWidget.draggingEnabled = !value;
this.mapSettings.isReadOnly = value;
}
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {

View File

@ -12,9 +12,11 @@ import { MapNode } from '../../models/map/map-node';
import { MapDrawing } from '../../models/map/map-drawing';
import { DraggedDataEvent } from '../../events/event-source';
import { select } from 'd3-selection';
import { NodeWidget } from '../../widgets/node';
import { MapLabel } from '../../models/map/map-label';
import { LabelWidget } from '../../widgets/label';
import { InterfaceLabelWidget } from '../../widgets/interface-label';
import { MapLinkNode } from '../../models/map/map-link-node';
import { LinksEventSource } from '../../events/links-event-source';
@Component({
selector: 'app-draggable-selection',
@ -33,10 +35,12 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
private drawingsWidget: DrawingsWidget,
private linksWidget: LinksWidget,
private labelWidget: LabelWidget,
private interfaceWidget: InterfaceLabelWidget,
private selectionManager: SelectionManager,
private nodesEventSource: NodesEventSource,
private drawingsEventSource: DrawingsEventSource,
private graphDataManager: GraphDataManager
private graphDataManager: GraphDataManager,
private linksEventSource: LinksEventSource
) { }
ngOnInit() {
@ -45,7 +49,8 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
this.start = merge(
this.nodesWidget.draggable.start,
this.drawingsWidget.draggable.start,
this.labelWidget.draggable.start
this.labelWidget.draggable.start,
this.interfaceWidget.draggable.start
).subscribe((evt: DraggableStart<any>) => {
const selected = this.selectionManager.getSelected();
if (evt.datum instanceof MapNode) {
@ -65,12 +70,19 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
this.selectionManager.setSelected([evt.datum]);
}
}
if (evt.datum instanceof MapLinkNode) {
if (selected.filter((item) => item instanceof MapLinkNode && item.id === evt.datum.id).length === 0) {
this.selectionManager.setSelected([evt.datum]);
}
}
});
this.drag = merge(
this.nodesWidget.draggable.drag,
this.drawingsWidget.draggable.drag,
this.labelWidget.draggable.drag
this.labelWidget.draggable.drag,
this.interfaceWidget.draggable.drag
).subscribe((evt: DraggableDrag<any>) => {
const selected = this.selectionManager.getSelected();
const selectedNodes = selected.filter((item) => item instanceof MapNode);
@ -108,12 +120,33 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
this.labelWidget.redrawLabel(svg, label);
});
// update interface labels
selected.filter((item) => item instanceof MapLinkNode).forEach((interfaceLabel: MapLinkNode) => {
const isParentNodeSelected = selectedNodes.filter((node) => node.id === interfaceLabel.nodeId).length > 0;
if (isParentNodeSelected) {
return;
}
const link = this.graphDataManager.getLinks().filter((link) => link.nodes[0].id === interfaceLabel.id || link.nodes[1].id === interfaceLabel.id)[0];
if(link.nodes[0].id === interfaceLabel.id) {
link.nodes[0].label.x += evt.dx;
link.nodes[0].label.y += evt.dy;
}
if(link.nodes[1].id === interfaceLabel.id) {
link.nodes[1].label.x += evt.dx;
link.nodes[1].label.y += evt.dy;
}
this.linksWidget.redrawLink(svg, link);
});
});
this.end = merge(
this.nodesWidget.draggable.end,
this.drawingsWidget.draggable.end,
this.labelWidget.draggable.end,
this.interfaceWidget.draggable.end
).subscribe((evt: DraggableEnd<any>) => {
const selected = this.selectionManager.getSelected();
const selectedNodes = selected.filter((item) => item instanceof MapNode);
@ -135,7 +168,17 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
this.nodesEventSource.labelDragged.emit(new DraggedDataEvent<MapLabel>(label, evt.dx, evt.dy));
});
selected.filter((item) => item instanceof MapLinkNode).forEach((label: MapLinkNode) => {
const isParentNodeSelected = selectedNodes.filter((node) => node.id === label.nodeId).length > 0;
if (isParentNodeSelected) {
return;
}
this.linksEventSource.interfaceDragged.emit(new DraggedDataEvent<MapLinkNode>(label, evt.dx, evt.dy));
});
});
}
ngOnDestroy() {

View File

@ -42,7 +42,7 @@ export class DrawLinkToolComponent implements OnInit, OnDestroy {
if (this.drawingLineTool.isDrawing()) {
this.drawingLineTool.stop();
}
this.onNodeClicked.unsubscribe();
// this.onNodeClicked.unsubscribe();
}
public onChooseInterface(event) {

View File

@ -9,6 +9,6 @@
*ngFor="let line of lines; index as i"
xml:space="preserve"
x="0"
[attr.dy]="i == 0 ? '0em' : '1.2em'"
[attr.dy]="i == 0 ? '0em' : '1.4em'"
>{{line}}</svg:tspan>
</svg:text>

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 344 B

View File

@ -44,11 +44,35 @@ export class SelectionControlComponent implements OnInit, OnDestroy {
return this.inRectangleHelper.inRectangle(rectangle, labelX, labelY);
}).map((node) => node.label);
const selectedInterfacesLabelsSources = this.graphDataManager.getLinks().filter((link) => {
if (link.source === undefined || link.nodes.length != 2 || link.nodes[0].label === undefined) {
return false;
}
const interfaceLabelX = link.source.x + link.nodes[0].label.x;
const interfaceLabelY = link.source.y + link.nodes[0].label.y;
return this.inRectangleHelper.inRectangle(rectangle, interfaceLabelX, interfaceLabelY);
}).map((link) => link.nodes[0]);
const selectedInterfacesLabelsTargets = this.graphDataManager.getLinks().filter((link) => {
if (link.target === undefined || link.nodes.length != 2 || link.nodes[1].label === undefined) {
return false;
}
const interfaceLabelX = link.target.x + link.nodes[1].label.x;
const interfaceLabelY = link.target.y + link.nodes[1].label.y;
return this.inRectangleHelper.inRectangle(rectangle, interfaceLabelX, interfaceLabelY);
}).map((link) => link.nodes[1]);
const selectedInterfaces = [
...selectedInterfacesLabelsSources,
...selectedInterfacesLabelsTargets,
]
const selected = [
...selectedNodes,
...selectedLinks,
...selectedDrawings,
...selectedLabels
...selectedLabels,
...selectedInterfaces,
];
this.selectionManager.setSelected(selected);

View File

@ -9,15 +9,21 @@ import { MapLinkNode } from "../../models/map/map-link-node";
@Injectable()
export class LinkNodeToMapLinkNodeConverter implements Converter<LinkNode, MapLinkNode> {
constructor(
private labelToMapLabel: LabelToMapLabelConverter
private labelToMapLabel: LabelToMapLabelConverter,
) {}
convert(linkNode: LinkNode) {
convert(linkNode: LinkNode, paramaters?: {[link_id: string]: string}) {
const mapLinkNode = new MapLinkNode();
mapLinkNode.nodeId = linkNode.node_id;
mapLinkNode.adapterNumber = linkNode.adapter_number;
mapLinkNode.portNumber = linkNode.port_number;
mapLinkNode.label = this.labelToMapLabel.convert(linkNode.label);
if (paramaters !== undefined) {
mapLinkNode.linkId = paramaters.link_id;
mapLinkNode.id = `${mapLinkNode.nodeId}-${mapLinkNode.linkId}`;
}
return mapLinkNode;
}
}

View File

@ -19,7 +19,7 @@ export class LinkToMapLinkConverter implements Converter<Link, MapLink> {
mapLink.captureFilePath = link.capture_file_path;
mapLink.capturing = link.capturing;
mapLink.linkType = link.link_type;
mapLink.nodes = link.nodes.map((linkNode) => this.linkNodeToMapLinkNode.convert(linkNode));
mapLink.nodes = link.nodes.map((linkNode) => this.linkNodeToMapLinkNode.convert(linkNode,{ link_id: link.link_id }));
mapLink.projectId = link.project_id;
return mapLink;
}

View File

@ -7,13 +7,13 @@ import { MapLabel } from "../../models/map/map-label";
@Injectable()
export class MapLabelToLabelConverter implements Converter<MapLabel, Label> {
convert(mapLabel: MapLabel, paramters?: any) {
const label = new Label();
label.rotation = mapLabel.rotation;
label.style = mapLabel.style;
label.text = mapLabel.text;
label.x = mapLabel.x;
label.y = mapLabel.y;
return label;
}
convert(mapLabel: MapLabel) {
const label = new Label();
label.rotation = mapLabel.rotation;
label.style = mapLabel.style;
label.text = mapLabel.text;
label.x = mapLabel.x;
label.y = mapLabel.y;
return label;
}
}

View File

@ -1,8 +1,11 @@
import { Injectable, EventEmitter } from "@angular/core";
import { MapLinkCreated } from "./links";
import { MapLinkNode } from "../models/map/map-link-node";
import { DraggedDataEvent } from "./event-source";
@Injectable()
export class LinksEventSource {
public created = new EventEmitter<MapLinkCreated>();
public interfaceDragged = new EventEmitter<DraggedDataEvent<MapLinkNode>>();
}

View File

@ -0,0 +1,7 @@
import { Injectable } from "@angular/core";
@Injectable()
export class MapSettingsManager {
public isReadOnly = false;
}

View File

@ -1,11 +0,0 @@
export class InterfaceLabel {
constructor(
public link_id: string,
public direction: string,
public x: number,
public y: number,
public text: string,
public style: string,
public rotation = 0,
) {}
}

View File

@ -1,8 +1,11 @@
import { MapLabel } from "./map-label";
import { Indexed } from "../../datasources/map-datasource";
export class MapLinkNode {
nodeId: string;
adapterNumber: number;
portNumber: number;
label: MapLabel;
export class MapLinkNode implements Indexed {
id: string;
nodeId: string;
linkId: string;
adapterNumber: number;
portNumber: number;
label: MapLabel;
}

View File

@ -13,6 +13,7 @@ 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()
@ -29,7 +30,8 @@ export class DrawingsWidget implements Widget {
constructor(
private drawingWidget: DrawingWidget,
private svgToDrawingConverter: SvgToDrawingConverter,
private context: Context
private context: Context,
private mapSettings: MapSettingsManager
) {
this.svgToDrawingConverter = new SvgToDrawingConverter();
}
@ -67,7 +69,7 @@ export class DrawingsWidget implements Widget {
.exit()
.remove();
if (this.draggingEnabled) {
if (!this.mapSettings.isReadOnly) {
this.draggable.call(merge);
}

View File

@ -67,7 +67,7 @@ describe('TextDrawingWidget', () => {
expect(drew.nodes()[1].innerHTML).toEqual('IS TEXT');
expect(drew.nodes()[1].getAttribute('x')).toEqual('0');
expect(drew.nodes()[1].getAttribute('dy')).toEqual('1.2em');
expect(drew.nodes()[1].getAttribute('dy')).toEqual('1.4em');
});
it('should draw whitespaces', () => {

View File

@ -64,7 +64,7 @@ export class TextDrawingWidget implements DrawingShapeWidget {
.text((line) => line)
.attr('xml:space', 'preserve')
.attr('x', 0)
.attr("dy", (line, i) => i === 0 ? '0em' : '1.2em');
.attr("dy", (line, i) => i === 0 ? '0em' : '1.4em');
lines
.exit()

View File

@ -1,7 +1,6 @@
import { Selection } from "d3-selection";
import { TestSVGCanvas } from "../testing";
import { InterfaceLabel } from "../models/interface-label";
import { InterfaceLabelWidget } from "./interface-label";
import { CssFixer } from "../helpers/css-fixer";
import { MapNode } from "../models/map/map-node";
@ -9,6 +8,8 @@ import { MapLink } from "../models/map/map-link";
import { MapLinkNode } from "../models/map/map-link-node";
import { MapLabel } from "../models/map/map-label";
import { FontFixer } from "../helpers/font-fixer";
import { SelectionManager } from "../managers/selection-manager";
import { MapSettingsManager } from "../managers/map-settings-manager";
describe('InterfaceLabelsWidget', () => {
@ -16,6 +17,7 @@ describe('InterfaceLabelsWidget', () => {
let widget: InterfaceLabelWidget;
let linksEnter: Selection<SVGGElement, MapLink, SVGGElement, any>;
let links: MapLink[];
let mapSettings: MapSettingsManager;
beforeEach(() => {
svg = new TestSVGCanvas();
@ -68,7 +70,8 @@ describe('InterfaceLabelsWidget', () => {
.exit()
.remove();
widget = new InterfaceLabelWidget(new CssFixer(), new FontFixer());
mapSettings = new MapSettingsManager();
widget = new InterfaceLabelWidget(new CssFixer(), new FontFixer(), new SelectionManager(), mapSettings);
});
afterEach(() => {
@ -78,24 +81,22 @@ describe('InterfaceLabelsWidget', () => {
it('should draw interface labels', () => {
widget.draw(linksEnter);
const drew = svg.canvas.selectAll<SVGGElement, InterfaceLabel>('g.interface_label_container');
const drew = svg.canvas.selectAll<SVGGElement, MapLinkNode>('g.interface_label_container');
expect(drew.nodes().length).toEqual(2);
const sourceInterface = drew.nodes()[0] as Element;
expect(sourceInterface.getAttribute('transform')).toEqual('translate(110, 220) rotate(5, 110, 220)');
const sourceIntefaceRect = sourceInterface.firstChild as Element;
expect(sourceIntefaceRect.attributes.getNamedItem('class').value).toEqual('interface_label_border');
expect(sourceIntefaceRect.attributes.getNamedItem('class').value).toEqual('interface_label_selection');
const sourceIntefaceText = sourceInterface.children[1];
expect(sourceIntefaceText.attributes.getNamedItem('class').value).toEqual('interface_label noselect');
expect(sourceIntefaceText.attributes.getNamedItem('style').value).toEqual('font-size:12px');
const targetInterface = drew.nodes()[1];
expect(targetInterface.getAttribute('transform')).toEqual('translate(270, 360) rotate(0, 270, 360)');
const targetIntefaceRect = targetInterface.firstChild as Element;
expect(targetIntefaceRect.attributes.getNamedItem('class').value).toEqual('interface_label_border');
expect(targetIntefaceRect.attributes.getNamedItem('class').value).toEqual('interface_label_selection');
const targetIntefaceText = targetInterface.children[1] as Element;
expect(targetIntefaceText.attributes.getNamedItem('class').value).toEqual('interface_label noselect');
expect(targetIntefaceText.attributes.getNamedItem('style').value).toEqual('');
@ -106,7 +107,7 @@ describe('InterfaceLabelsWidget', () => {
widget.setEnabled(false);
widget.draw(linksEnter);
const drew = svg.canvas.selectAll<SVGGElement, InterfaceLabel>('g.interface_label_container');
const drew = svg.canvas.selectAll<SVGGElement, MapLinkNode>('g.interface_label_container');
expect(drew.nodes().length).toEqual(0);
});

View File

@ -1,55 +1,59 @@
import { Injectable } from "@angular/core";
import { SVGSelection } from "../models/types";
import { InterfaceLabel } from "../models/interface-label";
import { CssFixer } from "../helpers/css-fixer";
import { select } from "d3-selection";
import { MapLink } from "../models/map/map-link";
import { FontFixer } from "../helpers/font-fixer";
import { SelectionManager } from "../managers/selection-manager";
import { MapLinkNode } from "../models/map/map-link-node";
import { MapNode } from "../models/map/map-node";
import { Draggable } from "../events/draggable";
import { MapSettingsManager } from "../managers/map-settings-manager";
@Injectable()
export class InterfaceLabelWidget {
static SURROUNDING_TEXT_BORDER = 5;
public draggable = new Draggable<SVGGElement, MapLinkNode>();
static SURROUNDING_TEXT_BORDER = 5;
private enabled = true;
constructor(
private cssFixer: CssFixer,
private fontFixer: FontFixer
private fontFixer: FontFixer,
private selectionManager: SelectionManager,
private mapSettings: MapSettingsManager
) {
}
public setEnabled(enabled: boolean) {
public setEnabled(enabled) {
this.enabled = enabled;
}
draw(selection: SVGSelection) {
const link_node_position = selection
.selectAll<SVGGElement, MapLinkNode>('g.link_node_position')
.data((link: MapLink) => [
[link.source, link.nodes[0]],
[link.target, link.nodes[1]]
]);
const labels = selection
.selectAll<SVGGElement, InterfaceLabel>('g.interface_label_container')
.data((l: MapLink) => {
const sourceInterface = new InterfaceLabel(
l.id,
'source',
Math.round(l.source.x + l.nodes[0].label.x),
Math.round(l.source.y + l.nodes[0].label.y),
l.nodes[0].label.text,
l.nodes[0].label.style,
l.nodes[0].label.rotation
);
const enter_link_node_position = link_node_position
.enter()
.append<SVGGElement>('g')
.classed('link_node_position', true);
const targetInterface = new InterfaceLabel(
l.id,
'target',
Math.round( l.target.x + l.nodes[1].label.x),
Math.round( l.target.y + l.nodes[1].label.y),
l.nodes[1].label.text,
l.nodes[1].label.style,
l.nodes[1].label.rotation
);
const merge_link_node_position = link_node_position.merge(enter_link_node_position);
merge_link_node_position.attr('transform', (nodeAndMapLinkNode: [MapNode, MapLinkNode]) => {
return `translate(${nodeAndMapLinkNode[0].x}, ${nodeAndMapLinkNode[0].y})`;
});
const labels = merge_link_node_position
.selectAll<SVGGElement, [MapNode, MapLinkNode]>('g.interface_label_container')
.data((nodeAndMapLinkNode: [MapNode, MapLinkNode]) => {
if (this.enabled) {
return [sourceInterface, targetInterface];
return [nodeAndMapLinkNode[1]];
}
return [];
});
@ -62,63 +66,65 @@ export class InterfaceLabelWidget {
// create surrounding rect
enter
.append<SVGRectElement>('rect')
.attr('class', 'interface_label_border');
.attr('class', 'interface_label_selection');
// create label
enter
.append<SVGTextElement>('text')
.attr('class', 'interface_label noselect');
.attr('class', 'interface_label noselect')
.attr('interface_label_id', (i: MapLinkNode) => `${i.id}`)
const merge = labels
.merge(enter);
merge
.attr('width', 100)
.attr('height', 100)
.attr('transform', function(this: SVGGElement, l: InterfaceLabel) {
const bbox = this.getBBox();
const x = l.x;
const y = l.y + bbox.height;
return `translate(${x}, ${y}) rotate(${l.rotation}, ${x}, ${y})`;
})
.classed('selected', (l: InterfaceLabel) => false);
// update label
merge
.select<SVGTextElement>('text.interface_label')
.text((l: InterfaceLabel) => l.text)
.attr('style', (l: InterfaceLabel) => {
let styles = this.cssFixer.fix(l.style);
.text((l: MapLinkNode) => l.label.text)
.attr('style', (l: MapLinkNode) => {
let styles = this.cssFixer.fix(l.label.style);
styles = this.fontFixer.fixStyles(styles);
return styles;
})
.attr('x', -InterfaceLabelWidget.SURROUNDING_TEXT_BORDER)
.attr('y', -InterfaceLabelWidget.SURROUNDING_TEXT_BORDER);
.attr('x', function (this: SVGTextElement, l: MapLinkNode) {
return l.label.x;
})
.attr('y', function (this: SVGTextElement, l: MapLinkNode) {
let bbox = this.getBBox();
return l.label.y + bbox.height;
})
.attr('transform', (l: MapLinkNode) => {
return `rotate(${l.label.rotation}, ${l.label.x}, ${l.label.y})`;
})
// update surrounding rect
merge
.select<SVGRectElement>('rect.interface_label_border')
.attr('visibility', (l: InterfaceLabel) => false ? 'visible' : 'hidden')
.attr('stroke-dasharray', '3,3')
.attr('stroke-width', '0.5')
.each(function (this: SVGRectElement, l: InterfaceLabel) {
const current = select(this);
const parent = select(this.parentElement);
const text = parent.select<SVGTextElement>('text');
const bbox = text.node().getBBox();
.select<SVGRectElement>('rect.interface_label_selection')
.attr('visibility', (l: MapLinkNode) => this.selectionManager.isSelected(l) ? 'visible' : 'hidden')
.attr('stroke', 'black')
.attr('stroke-dasharray', '3,3')
.attr('stroke-width', '0.5')
.attr('fill', 'none')
.each(function (this: SVGRectElement, l: MapLinkNode) {
const current = select(this);
const textLabel = merge.select<SVGTextElement>(`text[interface_label_id="${l.id}"]`);
const bbox = textLabel.node().getBBox();
const border = 2;
const border = InterfaceLabelWidget.SURROUNDING_TEXT_BORDER;
current.attr('width', bbox.width + border * 2);
current.attr('height', bbox.height + border);
current.attr('x', - border);
current.attr('y', - bbox.height);
});
current.attr('width', bbox.width + border * 2);
current.attr('height', bbox.height + border * 2);
current.attr('x', bbox.x - border);
current.attr('y', bbox.y - border);
current.attr('transform', `rotate(${l.label.rotation}, ${bbox.x - border}, ${bbox.y - border})`);
});
labels
.exit()
.remove();
if(!this.mapSettings.isReadOnly) {
this.draggable.call(merge);
}
}
}

View File

@ -9,6 +9,7 @@ import { MapNode } from "../models/map/map-node";
import { SelectionManager } from "../managers/selection-manager";
import { Draggable } from "../events/draggable";
import { MapLabel } from "../models/map/map-label";
import { MapSettingsManager } from "../managers/map-settings-manager";
@Injectable()
@ -20,7 +21,8 @@ export class LabelWidget implements Widget {
constructor(
private cssFixer: CssFixer,
private fontFixer: FontFixer,
private selectionManager: SelectionManager
private selectionManager: SelectionManager,
private mapSettings: MapSettingsManager,
) {}
public redrawLabel(view: SVGSelection, label: MapLabel) {
@ -47,7 +49,9 @@ export class LabelWidget implements Widget {
.exit()
.remove();
this.draggable.call(label_view);
if(!this.mapSettings.isReadOnly) {
this.draggable.call(label_view);
}
}
@ -96,7 +100,7 @@ export class LabelWidget implements Widget {
// bbox = this.getBBox();
// return - n.height / 2. - bbox.height ;
// }
return l.y + bbox.height - LabelWidget.NODE_LABEL_MARGIN;
return l.y + bbox.height - LabelWidget.NODE_LABEL_MARGIN - bbox.height*0.14;
})
.attr('transform', (l: MapLabel) => {
return `rotate(${l.rotation}, ${l.x}, ${l.y})`;

View File

@ -3,6 +3,7 @@ import { TestSVGCanvas } from "../testing";
import { NodesWidget } from "./nodes";
import { NodeWidget } from "./node";
import { instance, mock } from "ts-mockito";
import { MapSettingsManager } from "../managers/map-settings-manager";
describe('NodesWidget', () => {
@ -13,7 +14,7 @@ describe('NodesWidget', () => {
beforeEach(() => {
svg = new TestSVGCanvas();
nodeWidget = instance(mock(NodeWidget));
widget = new NodesWidget(nodeWidget);
widget = new NodesWidget(nodeWidget, new MapSettingsManager());
});
afterEach(() => {

View File

@ -6,6 +6,7 @@ import { Layer } from "../models/layer";
import { NodeWidget } from "./node";
import { Draggable } from "../events/draggable";
import { MapNode } from "../models/map/map-node";
import { MapSettingsManager } from "../managers/map-settings-manager";
@Injectable()
@ -13,10 +14,10 @@ export class NodesWidget implements Widget {
static NODE_LABEL_MARGIN = 3;
public draggable = new Draggable<SVGGElement, MapNode>();
public draggingEnabled = false;
constructor(
private nodeWidget: NodeWidget
private nodeWidget: NodeWidget,
private mapSettings: MapSettingsManager
) {
}
@ -49,7 +50,7 @@ export class NodesWidget implements Widget {
.exit()
.remove();
if (this.draggingEnabled) {
if (!this.mapSettings.isReadOnly) {
this.draggable.call(merge);
}
}

View File

@ -44,6 +44,7 @@ import { LineElement } from '../../cartography/models/drawings/line-element';
import { SettingsService, Settings } from '../../services/settings.service';
import { MapLabel } from '../../cartography/models/map/map-label';
import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component';
import { MapLinkNode } from '../../cartography/models/map/map-link-node';
@Component({
@ -193,6 +194,10 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.subscriptions.push(
this.linksEventSource.created.subscribe((evt) => this.onLinkCreated(evt))
);
this.subscriptions.push(
this.linksEventSource.interfaceDragged.subscribe((evt) => this.onInterfaceLabelDragged(evt))
);
}
onProjectLoad(project: Project) {
@ -295,6 +300,24 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
});
}
private onInterfaceLabelDragged(draggedEvent: DraggedDataEvent<MapLinkNode>) {
const link = this.linksDataSource.get(draggedEvent.datum.linkId);
if (link.nodes[0].node_id === draggedEvent.datum.nodeId) {
link.nodes[0].label.x += draggedEvent.dx;
link.nodes[0].label.y += draggedEvent.dy;
}
if (link.nodes[1].node_id === draggedEvent.datum.nodeId) {
link.nodes[1].label.x += draggedEvent.dx;
link.nodes[1].label.y += draggedEvent.dy;
}
this.linkService
.updateNodes(this.server, link, link.nodes)
.subscribe((serverLink: Link) => {
this.linksDataSource.update(serverLink);
});
}
private onLinkCreated(linkCreated: MapLinkCreated) {
const sourceNode = this.mapNodeToNode.convert(linkCreated.sourceNode);
const sourcePort = this.mapPortToPort.convert(linkCreated.sourcePort);

View File

@ -2,8 +2,8 @@
<div class="default-header">
<div class="row">
<h1 class="col">Projects</h1>
<button class="col" mat-raised-button color="primary" (click)="addBlankProject()" class="add-button">Add blank project</button>
<button class="col" mat-raised-button color="primary" (click)="importProject()" class="import-button">Import project</button>
<button *ngIf="settings.experimental_features" class="col" mat-raised-button color="primary" (click)="addBlankProject()" class="add-button">Add blank project</button>
<button *ngIf="settings.experimental_features" class="col" mat-raised-button color="primary" (click)="importProject()" class="import-button">Import project</button>
</div>
</div>
<div class="default-content">

View File

@ -5,6 +5,8 @@ import 'rxjs/add/operator/map';
import { Server } from "../models/server";
import { HttpServer } from "./http-server.service";
import {Port} from "../models/port";
import { Link } from '../models/link';
import { LinkNode } from '../models/link-node';
@Injectable()
export class LinkService {
@ -33,4 +35,27 @@ export class LinkService {
]});
}
updateNodes(
server: Server, link: Link, nodes: LinkNode[]) {
const requestNodes = nodes.map((linkNode) => {
return {
node_id: linkNode.node_id,
port_number: linkNode.port_number,
adapter_number: linkNode.adapter_number,
label: {
rotation: linkNode.label.rotation,
style: linkNode.label.style,
text: linkNode.label.text,
x: linkNode.label.x,
y: linkNode.label.y
}
}
});
return this.httpServer
.put(
server,
`/projects/${link.project_id}/links/${link.link_id}`,
{"nodes": requestNodes});
}
}