WIP Networklink POC

This commit is contained in:
fbsanchez 2020-10-29 17:20:16 +01:00
parent 27ba6a3adf
commit 8e09a30a05
13 changed files with 303 additions and 517 deletions

View File

@ -119,6 +119,7 @@ foreach ($layoutDatas as $layoutData) {
}
switch ($layoutData['type']) {
case NETWORK_LINK:
case LINE_ITEM:
visual_map_print_user_line_handles($layoutData);
visual_map_print_user_lines($layoutData);

View File

@ -271,6 +271,7 @@ foreach ($layoutDatas as $layoutData) {
);
break;
case NETWORK_LINK:
case LINE_ITEM:
$table->data[($i + 1)]['icon'] = html_print_image(
'images/line_item.png',
@ -303,6 +304,7 @@ foreach ($layoutDatas as $layoutData) {
switch ($layoutData['type']) {
case ICON:
case BOX_ITEM:
case NETWORK_LINK:
case LINE_ITEM:
// hasn't the label.
$table->data[($i + 1)][0] = '';
@ -345,6 +347,7 @@ foreach ($layoutDatas as $layoutData) {
// Width and height
switch ($layoutData['type']) {
case NETWORK_LINK:
case LINE_ITEM:
// hasn't the width and height.
$table->data[($i + 1)][2] = '';
@ -361,6 +364,7 @@ foreach ($layoutDatas as $layoutData) {
// Position
switch ($layoutData['type']) {
case NETWORK_LINK:
case LINE_ITEM:
// hasn't the width and height.
$table->data[($i + 1)][3] = '';
@ -375,6 +379,7 @@ foreach ($layoutDatas as $layoutData) {
// Parent
switch ($layoutData['type']) {
case BOX_ITEM:
case NETWORK_LINK:
case LINE_ITEM:
case COLOR_CLOUD:
$table->data[($i + 1)][4] = '';
@ -434,6 +439,7 @@ foreach ($layoutDatas as $layoutData) {
case BOX_ITEM:
case ICON:
case LABEL:
case NETWORK_LINK:
case LINE_ITEM:
$table->data[($i + 2)][0] = '';
break;
@ -494,6 +500,7 @@ foreach ($layoutDatas as $layoutData) {
case ICON:
case LABEL:
case BOX_ITEM:
case NETWORK_LINK:
case LINE_ITEM:
case GROUP_ITEM:
$table->data[($i + 2)][1] = '';
@ -598,6 +605,7 @@ foreach ($layoutDatas as $layoutData) {
// Map linked
switch ($layoutData['type']) {
case NETWORK_LINK:
case LINE_ITEM:
case BOX_ITEM:
case AUTO_SLA_GRAPH:

View File

@ -1449,6 +1449,16 @@ switch ($action) {
}
switch ($type) {
case 'network_link':
$values['type'] = NETWORK_LINK;
$values['border_width'] = $line_width;
$values['border_color'] = $line_color;
$values['pos_x'] = $line_start_x;
$values['pos_y'] = $line_start_y;
$values['width'] = $line_end_x;
$values['height'] = $line_end_y;
break;
case 'line_item':
$values['type'] = LINE_ITEM;
$values['border_width'] = $line_width;

View File

@ -3670,6 +3670,7 @@ function visual_map_print_visual_map(
$layout_data['label'] = visual_map_macro($layout_data['label'], $layout_data['id_agente_modulo']);
switch ($layout_data['type']) {
case NETWORK_LINK:
case LINE_ITEM:
visual_map_print_user_lines($layout_data, $proportion);
break;
@ -4382,6 +4383,9 @@ function visual_map_type_in_js($type)
case LINE_ITEM:
return 'line_item';
case NETWORK_LINK:
return 'network_link';
case COLOR_CLOUD:
return 'color_cloud';

View File

@ -186,13 +186,17 @@ function createVisualConsole(
var item = e.item || {};
var meta = item.meta || {};
if ((meta.editMode || meta.lineMode) && !meta.isUpdating) {
if (meta.editMode && !meta.isUpdating) {
createOrUpdateVisualConsoleItem(
visualConsole,
asyncTaskManager,
baseUrl,
item
);
} else if (meta.lineMode && item.props.type == 21) {
confirmDialog({
title: "todo"
});
}
});
// VC Item moved.
@ -203,7 +207,7 @@ function createVisualConsole(
y: e.newPosition.y,
type: e.item.props.type
};
if (e.item.props.type === 13) {
if (e.item.props.type === 13 || e.item.props.type === 21) {
var startIsLeft =
e.item.props.startPosition.x - e.item.props.endPosition.x <= 0;
var startIsTop =
@ -427,6 +431,7 @@ function createVisualConsole(
},
createItem: function(typeString) {
var type;
console.log(typeString);
switch (typeString) {
case "STATIC_GRAPH":
type = 0;
@ -479,6 +484,9 @@ function createVisualConsole(
case "COLOR_CLOUD":
type = 20;
break;
case "NETWORK_LINK":
type = 21;
break;
default:
type = 0;
}
@ -565,7 +573,7 @@ function createVisualConsole(
item.setMeta({ isUpdating: false });
var itemRetrieved = item.props;
if (itemRetrieved["type"] == 13) {
if (itemRetrieved["type"] == 13 || itemRetrieved["type"] == 21) {
var startIsLeft =
itemRetrieved["startPosition"]["x"] -
itemRetrieved["endPosition"]["x"] <=
@ -1179,6 +1187,9 @@ function createOrUpdateVisualConsoleItem(
case 20:
nameType = "Color Cloud";
break;
case 21:
nameType = "Network Link";
break;
default:
nameType = "Static graph";
@ -1259,7 +1270,8 @@ function createOrUpdateVisualConsoleItem(
tinyMCE != undefined &&
tinyMCE.editors.length > 0 &&
item.itemProps.type != 12 &&
item.itemProps.type != 13
item.itemProps.type != 13 &&
item.itemProps.type != 21
) {
// Content tiny.
var label = tinyMCE.activeEditor.getContent();

View File

@ -109,7 +109,7 @@ if ($getVisualConsole === true) {
$ratio
);
echo '['.implode($vcItems, ',').']';
echo '['.implode(',', $vcItems).']';
return;
} else if ($getVisualConsoleItem === true
|| $updateVisualConsoleItem === true
@ -245,7 +245,9 @@ if ($getVisualConsole === true) {
$item = VisualConsole::getItemFromDB($itemId);
$data = $item->toArray();
$data['id_layout'] = $visualConsoleId;
if ($data['type'] === LINE_ITEM) {
if ($data['type'] === LINE_ITEM
|| $data['type'] === NETWORK_LINK
) {
$data['endX'] = ($data['endX'] + 20);
$data['endY'] = ($data['endY'] + 20);
$data['startX'] = ($data['startX'] + 20);

View File

@ -186,6 +186,7 @@ final class NetworkLink extends Model
* Obtain a vc item data structure from the database using a filter.
*
* @param array $filter Filter of the Visual Console Item.
* @param float $ratio Adjustment ratio factor.
*
* @return array The Visual Console line data structure stored into the DB.
* @throws \Exception When the data cannot be retrieved from the DB.

View File

@ -81,7 +81,9 @@ class View extends \HTML
$activetabs = 2;
if ($type === LABEL) {
$activetabs = 0;
} else if ($type === LINE_ITEM) {
} else if ($type === LINE_ITEM
|| $type === NETWORK_LINK
) {
$activetabs = 0;
$tabs = [
[
@ -306,7 +308,7 @@ class View extends \HTML
);
} else {
// Only Create, settings default values if not enter tab general.
if ($itemId === 0 && $type != LINE_ITEM) {
if ($itemId === 0 && $type != LINE_ITEM && $type != NETWORK_LINK) {
$class = VisualConsole::getItemClass((int) $type);
$data = $class::getDefaultGeneralValues($data);
}
@ -491,6 +493,17 @@ class View extends \HTML
$data['isLinkEnabled'] = true;
break;
case NETWORK_LINK:
$data['borderColor'] = \get_parameter('borderColor');
$data['borderWidth'] = \get_parameter('borderWidth');
$data['isOnTop'] = \get_parameter_switch('isOnTop');
// Insert line default position ball end.
if ($itemId === 0) {
$data['height'] = 100;
$data['width'] = 100;
}
break;
default:
// Not posible.
break;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -843,6 +843,7 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
this.elementRef.classList.remove("is-editing");
}
}
if (!prevMeta || prevMeta.isFetching !== this.meta.isFetching) {
if (this.meta.isFetching) {
this.elementRef.classList.add("is-fetching");
@ -1003,8 +1004,13 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
*/
protected resizeElement(width: number, height: number): void {
// The most valuable size is the content size.
this.childElementRef.style.width = width > 0 ? `${width}px` : null;
this.childElementRef.style.height = height > 0 ? `${height}px` : null;
if (
this.props.type != ItemType.LINE_ITEM &&
this.props.type != ItemType.NETWORK_LINK
) {
this.childElementRef.style.width = width > 0 ? `${width}px` : "0";
this.childElementRef.style.height = height > 0 ? `${height}px` : "0";
}
if (this.props.label && this.props.label.length > 0) {
// Ugly table to show the label as its legacy counterpart.
@ -1015,11 +1021,11 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
switch (this.props.labelPosition) {
case "up":
case "down":
table.style.width = width > 0 ? `${width}px` : null;
table.style.width = width > 0 ? `${width}px` : "0";
break;
case "left":
case "right":
table.style.height = height > 0 ? `${height}px` : null;
table.style.height = height > 0 ? `${height}px` : "0";
break;
}
}
@ -1182,7 +1188,7 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
};
this.initMovementListener(this.elementRef);
if (this.props.type !== 13) {
if (this.props.type !== ItemType.LINE_ITEM) {
this.initResizementListener(this.elementRef);
}
}
@ -1198,7 +1204,7 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
};
this.stopMovementListener();
if (this.props.type !== 13) {
if (this.props.type !== ItemType.LINE_ITEM) {
this.stopResizementListener();
}
}

View File

@ -8,9 +8,9 @@ import {
import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item";
import TypedEvent, { Listener, Disposable } from "../lib/TypedEvent";
interface LineProps extends ItemProps {
export interface LineProps extends ItemProps {
// Overrided properties.
readonly type: ItemType.LINE_ITEM;
type: number;
label: null;
isLinkEnabled: false;
parentId: null;
@ -20,6 +20,8 @@ interface LineProps extends ItemProps {
endPosition: Position;
lineWidth: number;
color: string | null;
viewportOffsetX: number;
viewportOffsetY: number;
}
/**
@ -54,7 +56,9 @@ export function linePropsDecoder(data: AnyObject): LineProps | never {
y: parseIntOr(data.endY, 0)
},
lineWidth: parseIntOr(data.lineWidth || data.borderWidth, 1),
color: notEmptyStringOr(data.borderColor || data.color, null)
color: notEmptyStringOr(data.borderColor || data.color, null),
viewportOffsetX: 0,
viewportOffsetY: 0
};
/*
@ -82,20 +86,20 @@ export interface LineMovedEvent {
}
export default class Line extends Item<LineProps> {
private circleRadius = 8;
protected circleRadius = 8;
// To control if the line movement is enabled.
private moveMode: boolean = false;
protected moveMode: boolean = false;
// To control if the line is moving.
private isMoving: boolean = false;
protected isMoving: boolean = false;
// Event manager for moved events.
private readonly lineMovedEventManager = new TypedEvent<LineMovedEvent>();
protected readonly lineMovedEventManager = new TypedEvent<LineMovedEvent>();
// List of references to clean the event listeners.
private readonly lineMovedEventDisposables: Disposable[] = [];
protected readonly lineMovedEventDisposables: Disposable[] = [];
// This function will only run the 2nd arg function after the time
// of the first arg have passed after its last execution.
private debouncedStartPositionMovementSave = debounce(
protected debouncedStartPositionMovementSave = debounce(
500, // ms.
(x: Position["x"], y: Position["y"]) => {
this.isMoving = false;
@ -110,13 +114,13 @@ export default class Line extends Item<LineProps> {
);
// This property will store the function
// to clean the movement listener.
private removeStartPositionMovement: Function | null = null;
protected removeStartPositionMovement: Function | null = null;
/**
* Start the movement funtionality for the start position.
* @param element Element to move inside its container.
*/
private initStartPositionMovementListener(
protected initStartPositionMovementListener(
element: HTMLElement,
container: HTMLElement
): void {
@ -124,8 +128,8 @@ export default class Line extends Item<LineProps> {
element,
(x: Position["x"], y: Position["y"]) => {
// Calculate the center of the circle.
x += this.circleRadius;
y += this.circleRadius;
x += this.circleRadius - this.props.viewportOffsetX / 2;
y += this.circleRadius - this.props.viewportOffsetY / 2;
const startPosition = { x, y };
@ -153,7 +157,7 @@ export default class Line extends Item<LineProps> {
// This function will only run the 2nd arg function after the time
// of the first arg have passed after its last execution.
private debouncedEndPositionMovementSave = debounce(
protected debouncedEndPositionMovementSave = debounce(
500, // ms.
(x: Position["x"], y: Position["y"]) => {
this.isMoving = false;
@ -168,13 +172,13 @@ export default class Line extends Item<LineProps> {
);
// This property will store the function
// to clean the movement listener.
private removeEndPositionMovement: Function | null = null;
protected removeEndPositionMovement: Function | null = null;
/**
* End the movement funtionality for the end position.
* @param element Element to move inside its container.
*/
private initEndPositionMovementListener(
protected initEndPositionMovementListener(
element: HTMLElement,
container: HTMLElement
): void {
@ -182,8 +186,8 @@ export default class Line extends Item<LineProps> {
element,
(x: Position["x"], y: Position["y"]) => {
// Calculate the center of the circle.
x += this.circleRadius;
y += this.circleRadius;
x += this.circleRadius - this.props.viewportOffsetX / 2;
y += this.circleRadius - this.props.viewportOffsetY / 2;
this.isMoving = true;
this.props = {
@ -231,6 +235,11 @@ export default class Line extends Item<LineProps> {
this.moveMode = meta.editMode;
this.init();
super.resizeElement(
Math.max(props.width, props.viewportOffsetX),
Math.max(props.height, props.viewportOffsetY)
);
}
/**
@ -272,27 +281,33 @@ export default class Line extends Item<LineProps> {
const element: HTMLDivElement = document.createElement("div");
element.className = "line";
const {
let {
x, // Box x
y, // Box y
width, // Box width
height, // Box height
lineWidth, // Line thickness
lineWidth, // Line thickness,
viewportOffsetX, // viewport width,
viewportOffsetY, // viewport heigth,
startPosition, // Line start position
endPosition, // Line end position
color // Line color
} = this.props;
const x1 = startPosition.x - x + lineWidth / 2;
const y1 = startPosition.y - y + lineWidth / 2;
const x2 = endPosition.x - x + lineWidth / 2;
const y2 = endPosition.y - y + lineWidth / 2;
width = width + viewportOffsetX;
height = height + viewportOffsetY;
const x1 = startPosition.x - x + lineWidth / 2 + viewportOffsetX / 2;
const y1 = startPosition.y - y + lineWidth / 2 + viewportOffsetY / 2;
const x2 = endPosition.x - x + lineWidth / 2 + viewportOffsetX / 2;
const y2 = endPosition.y - y + lineWidth / 2 + viewportOffsetY / 2;
// SVG container.
const svg = document.createElementNS(svgNS, "svg");
// Set SVG size.
svg.setAttribute("width", `${width + lineWidth}`);
svg.setAttribute("height", `${height + lineWidth}`);
const line = document.createElementNS(svgNS, "line");
line.setAttribute("x1", `${x1}`);
line.setAttribute("y1", `${y1}`);
@ -308,21 +323,26 @@ export default class Line extends Item<LineProps> {
}
protected updateDomElement(element: HTMLElement): void {
const {
let {
x, // Box x
y, // Box y
width, // Box width
height, // Box height
lineWidth, // Line thickness
viewportOffsetX, // viewport width,
viewportOffsetY, // viewport heigth,
startPosition, // Line start position
endPosition, // Line end position
color // Line color
} = this.props;
const x1 = startPosition.x - x + lineWidth / 2;
const y1 = startPosition.y - y + lineWidth / 2;
const x2 = endPosition.x - x + lineWidth / 2;
const y2 = endPosition.y - y + lineWidth / 2;
width = width + viewportOffsetX;
height = height + viewportOffsetY;
const x1 = startPosition.x - x + lineWidth / 2 + viewportOffsetX / 2;
const y1 = startPosition.y - y + lineWidth / 2 + viewportOffsetY / 2;
const x2 = endPosition.x - x + lineWidth / 2 + viewportOffsetX / 2;
const y2 = endPosition.y - y + lineWidth / 2 + viewportOffsetY / 2;
const svgs = element.getElementsByTagName("svg");
@ -352,9 +372,6 @@ export default class Line extends Item<LineProps> {
}
if (this.moveMode) {
const startIsLeft = startPosition.x - endPosition.x <= 0;
const startIsTop = startPosition.y - endPosition.y <= 0;
let startCircle: HTMLElement = document.createElement("div");
let endCircle: HTMLElement = document.createElement("div");
@ -384,12 +401,8 @@ export default class Line extends Item<LineProps> {
startCircle.style.borderRadius = "50%";
startCircle.style.backgroundColor = `${color}`;
startCircle.style.position = "absolute";
startCircle.style.left = startIsLeft
? `-${this.circleRadius}px`
: `${width + lineWidth - this.circleRadius}px`;
startCircle.style.top = startIsTop
? `-${this.circleRadius}px`
: `${height + lineWidth - this.circleRadius}px`;
startCircle.style.left = `${x1 - this.circleRadius}px`;
startCircle.style.top = `${y1 - this.circleRadius}px`;
endCircle.classList.add(
"visual-console-item-line-circle",
@ -400,12 +413,8 @@ export default class Line extends Item<LineProps> {
endCircle.style.borderRadius = "50%";
endCircle.style.backgroundColor = `${color}`;
endCircle.style.position = "absolute";
endCircle.style.left = startIsLeft
? `${width + lineWidth - 8}px`
: `-${this.circleRadius}px`;
endCircle.style.top = startIsTop
? `${height + lineWidth - this.circleRadius}px`
: `-${this.circleRadius}px`;
endCircle.style.left = `${x2 - this.circleRadius}px`;
endCircle.style.top = `${y2 - this.circleRadius}px`;
if (element.parentElement !== null) {
const circles = element.parentElement.getElementsByClassName(

View File

@ -1,25 +1,13 @@
import { AnyObject, Position, Size, ItemMeta } from "../lib/types";
import {
parseIntOr,
notEmptyStringOr,
debounce,
addMovementListener
} from "../lib";
import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item";
import TypedEvent, { Listener, Disposable } from "../lib/TypedEvent";
import { AnyObject, Position, ItemMeta } from "../lib/types";
import { debounce, addMovementListener } from "../lib";
import { ItemType } from "../Item";
import Line, { LineProps, linePropsDecoder } from "./Line";
interface NetworkLinkProps extends ItemProps {
const svgNS = "http://www.w3.org/2000/svg";
export interface NetworkLinkProps extends LineProps {
// Overrided properties.
readonly type: ItemType.NETWORK_LINK;
label: null;
isLinkEnabled: false;
parentId: null;
aclGroupId: null;
// Custom properties.
startPosition: Position;
endPosition: Position;
lineWidth: number;
color: string | null;
type: number;
}
/**
@ -34,186 +22,17 @@ interface NetworkLinkProps extends ItemProps {
export function networkLinkPropsDecoder(
data: AnyObject
): NetworkLinkProps | never {
const props: NetworkLinkProps = {
...itemBasePropsDecoder({ ...data, width: 1, height: 1 }), // Object spread. It will merge the properties of the two objects.
type: ItemType.NETWORK_LINK,
label: null,
isLinkEnabled: false,
parentId: null,
aclGroupId: null,
// Initialize Position & Size.
x: 0,
y: 0,
width: 0,
height: 0,
// Custom properties.
startPosition: {
x: parseIntOr(data.startX, 0),
y: parseIntOr(data.startY, 0)
},
endPosition: {
x: parseIntOr(data.endX, 0),
y: parseIntOr(data.endY, 0)
},
lineWidth: parseIntOr(data.lineWidth || data.borderWidth, 1),
color: notEmptyStringOr(data.borderColor || data.color, null)
};
/*
* We need to enhance the props with the extracted size and position
* of the box cause there are missing at the props update. A better
* solution would be overriding the props setter to do it there, but
* the language doesn't allow it while targetting ES5.
* TODO: We need to figure out a more consistent solution.
*/
return {
...props,
// Enhance the props extracting the box size and position.
// eslint-disable-next-line @typescript-eslint/no-use-before-define
...NetworkLink.extractBoxSizeAndPosition(
props.startPosition,
props.endPosition
)
...linePropsDecoder(data), // Object spread. It will merge the properties of the two objects.
type: ItemType.NETWORK_LINK,
viewportOffsetX: 300,
viewportOffsetY: 300
};
}
const svgNS = "http://www.w3.org/2000/svg";
export interface NetworkLinkMovedEvent {
item: NetworkLink;
startPosition: NetworkLinkProps["startPosition"];
endPosition: NetworkLinkProps["endPosition"];
}
export default class NetworkLink extends Item<NetworkLinkProps> {
private circleRadius = 8;
// To control if the line movement is enabled.
private moveMode: boolean = false;
// To control if the line is moving.
private isMoving: boolean = false;
// Event manager for moved events.
private readonly lineMovedEventManager = new TypedEvent<
NetworkLinkMovedEvent
>();
// List of references to clean the event listeners.
private readonly lineMovedEventDisposables: Disposable[] = [];
// This function will only run the 2nd arg function after the time
// of the first arg have passed after its last execution.
private debouncedStartPositionMovementSave = debounce(
500, // ms.
(x: Position["x"], y: Position["y"]) => {
this.isMoving = false;
const startPosition = { x, y };
// Emit the movement event.
this.lineMovedEventManager.emit({
item: this,
startPosition,
endPosition: this.props.endPosition
});
}
);
// This property will store the function
// to clean the movement listener.
private removeStartPositionMovement: Function | null = null;
/**
* Start the movement funtionality for the start position.
* @param element Element to move inside its container.
*/
private initStartPositionMovementListener(
element: HTMLElement,
container: HTMLElement
): void {
this.removeStartPositionMovement = addMovementListener(
element,
(x: Position["x"], y: Position["y"]) => {
// Calculate the center of the circle.
x += this.circleRadius;
y += this.circleRadius;
const startPosition = { x, y };
this.isMoving = true;
this.props = {
...this.props,
startPosition
};
// Run the end function.
this.debouncedStartPositionMovementSave(x, y);
},
container
);
}
/**
* Stop the movement fun
*/
private stopStartPositionMovementListener(): void {
if (this.removeStartPositionMovement) {
this.removeStartPositionMovement();
this.removeStartPositionMovement = null;
}
}
// This function will only run the 2nd arg function after the time
// of the first arg have passed after its last execution.
private debouncedEndPositionMovementSave = debounce(
500, // ms.
(x: Position["x"], y: Position["y"]) => {
this.isMoving = false;
const endPosition = { x, y };
// Emit the movement event.
this.lineMovedEventManager.emit({
item: this,
endPosition,
startPosition: this.props.startPosition
});
}
);
// This property will store the function
// to clean the movement listener.
private removeEndPositionMovement: Function | null = null;
/**
* End the movement funtionality for the end position.
* @param element Element to move inside its container.
*/
private initEndPositionMovementListener(
element: HTMLElement,
container: HTMLElement
): void {
this.removeEndPositionMovement = addMovementListener(
element,
(x: Position["x"], y: Position["y"]) => {
// Calculate the center of the circle.
x += this.circleRadius;
y += this.circleRadius;
this.isMoving = true;
this.props = {
...this.props,
endPosition: { x, y }
};
// Run the end function.
this.debouncedEndPositionMovementSave(x, y);
},
container
);
}
/**
* Stop the movement function.
*/
private stopEndPositionMovementListener(): void {
if (this.removeEndPositionMovement) {
this.removeEndPositionMovement();
this.removeEndPositionMovement = null;
}
}
export default class NetworkLink extends Line {
private labelStart: string;
private labelEnd: string;
/**
* @override
*/
@ -224,112 +43,130 @@ export default class NetworkLink extends Item<NetworkLinkProps> {
*/
super(
{
...props,
...NetworkLink.extractBoxSizeAndPosition(
props.startPosition,
props.endPosition
)
...props
},
{
...meta
},
true
}
);
this.moveMode = meta.editMode;
this.init();
}
const x1 = props.startPosition.x - props.x + props.lineWidth / 2;
const y1 = props.startPosition.y - props.y + props.lineWidth / 2;
const x2 = props.endPosition.x - props.x + props.lineWidth / 2;
const y2 = props.endPosition.y - props.y + props.lineWidth / 2;
/**
* Classic and protected version of the setter of the `props` property.
* Useful to override it from children classes.
* @param newProps
* @override Item.setProps
*/
public setProps(newProps: NetworkLinkProps) {
super.setProps({
...newProps,
...NetworkLink.extractBoxSizeAndPosition(
newProps.startPosition,
newProps.endPosition
)
});
}
this.labelStart = `start (${x1},${y1})`;
this.labelEnd = `end (${x2},${y2})`;
/**
* Classic and protected version of the setter of the `meta` property.
* Useful to override it from children classes.
* @param newMetadata
* @override Item.setMeta
*/
public setMeta(newMetadata: ItemMeta) {
this.moveMode = newMetadata.editMode;
super.setMeta({
...newMetadata,
lineMode: true
});
this.render();
}
/**
* @override
* To create the item's DOM representation.
* @return Item.
*/
protected createDomElement(): HTMLElement {
const element: HTMLDivElement = document.createElement("div");
element.className = "line";
protected debouncedStartPositionMovementSave = debounce(
500, // ms.
(x: Position["x"], y: Position["y"]) => {
this.isMoving = false;
const startPosition = { x, y };
const {
x, // Box x
y, // Box y
width, // Box width
height, // Box height
lineWidth, // NetworkLink thickness
startPosition, // NetworkLink start position
endPosition, // NetworkLink end position
color // NetworkLink color
} = this.props;
this.labelStart = "start (" + x + "," + y + ")";
const x1 = startPosition.x - x + lineWidth / 2;
const y1 = startPosition.y - y + lineWidth / 2;
const x2 = endPosition.x - x + lineWidth / 2;
const y2 = endPosition.y - y + lineWidth / 2;
// Emit the movement event.
this.lineMovedEventManager.emit({
item: this,
startPosition,
endPosition: this.props.endPosition
});
}
);
// SVG container.
const svg = document.createElementNS(svgNS, "svg");
// Set SVG size.
svg.setAttribute("width", `${width + lineWidth}`);
svg.setAttribute("height", `${height + lineWidth}`);
const line = document.createElementNS(svgNS, "line");
line.setAttribute("x1", `${x1}`);
line.setAttribute("y1", `${y1}`);
line.setAttribute("x2", `${x2}`);
line.setAttribute("y2", `${y2}`);
line.setAttribute("stroke", color || "black");
line.setAttribute("stroke-width", `${lineWidth}`);
protected debouncedEndPositionMovementSave = debounce(
500, // ms.
(x: Position["x"], y: Position["y"]) => {
this.isMoving = false;
const endPosition = { x, y };
svg.append(line);
element.append(svg);
this.labelEnd = "end (" + x + "," + y + ")";
return element;
}
// Emit the movement event.
this.lineMovedEventManager.emit({
item: this,
endPosition,
startPosition: this.props.startPosition
});
}
);
protected updateDomElement(element: HTMLElement): void {
const {
super.updateDomElement(element);
let {
x, // Box x
y, // Box y
width, // Box width
height, // Box height
lineWidth, // NetworkLink thickness
startPosition, // NetworkLink start position
endPosition, // NetworkLink end position
color // NetworkLink color
lineWidth, // Line thickness
viewportOffsetX, // viewport width,
viewportOffsetY, // viewport heigth,
startPosition, // Line start position
endPosition, // Line end position
color // Line color
} = this.props;
const x1 = startPosition.x - x + lineWidth / 2;
const y1 = startPosition.y - y + lineWidth / 2;
const x2 = endPosition.x - x + lineWidth / 2;
const y2 = endPosition.y - y + lineWidth / 2;
// Font size and text adjustments.
const fontsize = 7.4;
const adjustment = 50;
// console.log(`startPosition [${startPosition.x},${startPosition.y}]`);
// console.log(`x.y [${x},${y}]`);
let x1 = startPosition.x - x + lineWidth / 2 + viewportOffsetX / 2;
let y1 = startPosition.y - y + lineWidth / 2 + viewportOffsetY / 2;
let x2 = endPosition.x - x + lineWidth / 2 + viewportOffsetX / 2;
let y2 = endPosition.y - y + lineWidth / 2 + viewportOffsetY / 2;
// Calculate angle (rotation).
let g = (Math.atan((y2 - y1) / (x2 - x1)) * 180) / Math.PI;
if (Math.abs(g) > 0) {
g = 0;
}
// Calculate effective 'text' box sizes.
const fontheight = 23;
let labelStartWidth = this.labelStart.length * fontsize;
let labelEndWidth = this.labelEnd.length * fontsize;
let labelStartHeight = fontheight;
let labelEndHeight = fontheight;
if (x1 < x2) {
// x1 on left of x2.
x1 += adjustment;
x2 -= adjustment + labelEndWidth;
}
if (x1 > x2) {
// x1 on right of x2.
x1 -= adjustment + labelStartWidth;
x2 += adjustment;
}
if (y1 < y2) {
// y1 on y2.
y1 += adjustment;
y2 -= adjustment + labelEndHeight;
}
if (y1 > y2) {
// y1 under y2.
y1 -= adjustment + labelStartHeight;
y2 += adjustment;
}
if (typeof color == "undefined") {
color = "#000";
}
// console.log(`to : ${x1},${y1} -------- ${x2}, ${y2}`);
// console.log(`inclinacion de ${g}`);
const svgs = element.getElementsByTagName("svg");
@ -338,193 +175,76 @@ export default class NetworkLink extends Item<NetworkLinkProps> {
if (svg != null) {
// Set SVG size.
svg.setAttribute("width", `${width + lineWidth}`);
svg.setAttribute("height", `${height + lineWidth}`);
const lines = svg.getElementsByTagNameNS(svgNS, "line");
let groups = svg.getElementsByTagNameNS(svgNS, "g");
while (groups.length > 0) {
groups[0].remove();
}
if (lines.length > 0) {
const line = lines.item(0);
if (line != null) {
line.setAttribute("x1", `${x1}`);
line.setAttribute("y1", `${y1}`);
line.setAttribute("x2", `${x2}`);
line.setAttribute("y2", `${y2}`);
line.setAttribute("stroke", color || "black");
line.setAttribute("stroke-width", `${lineWidth}`);
// let rect = document.createElementNS(
// "http://www.w3.org/2000/svg",
// "rect"
// );
// rect.setAttribute("x", SVGRect.x);
// rect.setAttribute("y", SVGRect.y);
// rect.setAttribute("width", SVGRect.width);
// rect.setAttribute("height", SVGRect.height);
// rect.setAttribute("fill", "yellow");
let start = document.createElementNS(svgNS, "g");
start.setAttribute("x", `${x1}`);
start.setAttribute("y", `${y1}`);
start.setAttribute("width", `${labelStartWidth + fontsize * 2}`);
start.setAttribute("height", `${labelStartHeight}`);
start.setAttribute("transform", `rotate(${g} ${x1} ${y1})`);
let sr = document.createElementNS(svgNS, "rect");
sr.setAttribute("x", `${x1}`);
sr.setAttribute("y", `${y1}`);
sr.setAttribute("width", `${labelStartWidth}`);
sr.setAttribute("height", `${labelStartHeight}`);
sr.setAttribute("stroke", `${color}`);
sr.setAttribute("stroke-width", "2");
sr.setAttribute("fill", "#FFF");
start.append(sr);
let st = document.createElementNS(svgNS, "text");
st.setAttribute("x", `${x1 + fontsize}`);
st.setAttribute("y", `${y1 + (fontheight * 2) / 3}`);
st.setAttribute("fill", "#000");
st.textContent = this.labelStart;
st.setAttribute("transform", `rotate(${g} ${x1} ${y1})`);
start.append(st);
let end = document.createElementNS(svgNS, "g");
let er = document.createElementNS(svgNS, "rect");
er.setAttribute("x", `${x2}`);
er.setAttribute("y", `${y2}`);
er.setAttribute("width", `${labelEndWidth + fontsize * 2}`);
er.setAttribute("height", `${labelEndHeight}`);
er.setAttribute("stroke", `${color}`);
er.setAttribute("stroke-width", "2");
er.setAttribute("fill", "#FFF");
er.setAttribute("transform", `rotate(${g} ${x1} ${y1})`);
end.append(er);
let et = document.createElementNS(svgNS, "text");
et.setAttribute("x", `${x2 + fontsize}`);
et.setAttribute("y", `${y2 + (fontheight * 2) / 3}`);
et.setAttribute("fill", "#000");
et.textContent = this.labelEnd;
et.setAttribute("transform", `rotate(${g} ${x1} ${y1})`);
end.append(et);
svg.append(start);
svg.append(end);
}
}
}
}
if (this.moveMode) {
const startIsLeft = startPosition.x - endPosition.x <= 0;
const startIsTop = startPosition.y - endPosition.y <= 0;
let startCircle: HTMLElement = document.createElement("div");
let endCircle: HTMLElement = document.createElement("div");
if (this.isMoving) {
const circlesStart = element.getElementsByClassName(
"visual-console-item-line-circle-start"
);
if (circlesStart.length > 0) {
const circle = circlesStart.item(0) as HTMLElement;
if (circle) startCircle = circle;
}
const circlesEnd = element.getElementsByClassName(
"visual-console-item-line-circle-end"
);
if (circlesEnd.length > 0) {
const circle = circlesEnd.item(0) as HTMLElement;
if (circle) endCircle = circle;
}
}
startCircle.classList.add(
"visual-console-item-line-circle",
"visual-console-item-line-circle-start"
);
startCircle.style.width = `${this.circleRadius * 2}px`;
startCircle.style.height = `${this.circleRadius * 2}px`;
startCircle.style.borderRadius = "50%";
startCircle.style.backgroundColor = `${color}`;
startCircle.style.position = "absolute";
startCircle.style.left = startIsLeft
? `-${this.circleRadius}px`
: `${width + lineWidth - this.circleRadius}px`;
startCircle.style.top = startIsTop
? `-${this.circleRadius}px`
: `${height + lineWidth - this.circleRadius}px`;
endCircle.classList.add(
"visual-console-item-line-circle",
"visual-console-item-line-circle-end"
);
endCircle.style.width = `${this.circleRadius * 2}px`;
endCircle.style.height = `${this.circleRadius * 2}px`;
endCircle.style.borderRadius = "50%";
endCircle.style.backgroundColor = `${color}`;
endCircle.style.position = "absolute";
endCircle.style.left = startIsLeft
? `${width + lineWidth - 8}px`
: `-${this.circleRadius}px`;
endCircle.style.top = startIsTop
? `${height + lineWidth - this.circleRadius}px`
: `-${this.circleRadius}px`;
if (element.parentElement !== null) {
const circles = element.parentElement.getElementsByClassName(
"visual-console-item-line-circle"
);
while (circles.length > 0) {
const circle = circles.item(0);
if (circle) circle.remove();
}
element.parentElement.appendChild(startCircle);
element.parentElement.appendChild(endCircle);
}
// Init the movement listeners.
this.initStartPositionMovementListener(startCircle, this.elementRef
.parentElement as HTMLElement);
this.initEndPositionMovementListener(endCircle, this.elementRef
.parentElement as HTMLElement);
} else if (!this.moveMode) {
this.stopStartPositionMovementListener();
// Remove circles.
if (element.parentElement !== null) {
const circles = element.parentElement.getElementsByClassName(
"visual-console-item-line-circle"
);
while (circles.length > 0) {
const circle = circles.item(0);
if (circle) circle.remove();
}
}
} else {
this.stopStartPositionMovementListener();
}
}
/**
* Extract the size and position of the box from
* the start and the finish of the line.
* @param props Item properties.
*/
public static extractBoxSizeAndPosition(
startPosition: Position,
endPosition: Position
): Size & Position {
return {
width: Math.abs(startPosition.x - endPosition.x),
height: Math.abs(startPosition.y - endPosition.y),
x: Math.min(startPosition.x, endPosition.x),
y: Math.min(startPosition.y, endPosition.y)
};
}
/**
* Update the position into the properties and move the DOM container.
* @param x Horizontal axis position.
* @param y Vertical axis position.
* @override item function
*/
public move(x: number, y: number): void {
super.moveElement(x, y);
const startIsLeft =
this.props.startPosition.x - this.props.endPosition.x <= 0;
const startIsTop =
this.props.startPosition.y - this.props.endPosition.y <= 0;
const start = {
x: startIsLeft ? x : this.props.width + x,
y: startIsTop ? y : this.props.height + y
};
const end = {
x: startIsLeft ? this.props.width + x : x,
y: startIsTop ? this.props.height + y : y
};
this.props = {
...this.props,
startPosition: start,
endPosition: end
};
}
/**
* To remove the event listeners and the elements from the DOM.
* @override Item.remove
*/
public remove(): void {
// Clear the item's event listeners.
this.stopStartPositionMovementListener();
// Call the parent's .remove()
super.remove();
}
/**
* To add an event handler to the movement of visual console elements.
* @param listener Function which is going to be executed when a linked console is moved.
*
* @override Item.onMoved
*/
public onNetworkLinkMovementFinished(
listener: Listener<NetworkLinkMovedEvent>
): Disposable {
/*
* The '.on' function returns a function which will clean the event
* listener when executed. We store all the 'dispose' functions to
* call them when the item should be cleared.
*/
const disposable = this.lineMovedEventManager.on(listener);
this.lineMovedEventDisposables.push(disposable);
return disposable;
}
}