2019-07-30 14:58:01 +02:00
|
|
|
import { AnyObject, Size, Position, WithModuleProps } from "./lib/types";
|
2019-03-08 13:32:56 +01:00
|
|
|
import {
|
|
|
|
parseBoolean,
|
|
|
|
sizePropsDecoder,
|
|
|
|
parseIntOr,
|
2019-06-04 13:17:19 +02:00
|
|
|
notEmptyStringOr,
|
2019-07-30 14:58:01 +02:00
|
|
|
itemMetaDecoder,
|
|
|
|
t,
|
|
|
|
ellipsize
|
2019-03-08 13:32:56 +01:00
|
|
|
} from "./lib";
|
2019-04-12 14:24:34 +02:00
|
|
|
import Item, {
|
|
|
|
ItemType,
|
|
|
|
ItemProps,
|
|
|
|
ItemClickEvent,
|
2019-06-11 16:44:19 +02:00
|
|
|
ItemRemoveEvent,
|
2019-06-17 12:22:55 +02:00
|
|
|
ItemMovedEvent,
|
2019-07-30 11:20:03 +02:00
|
|
|
ItemResizedEvent,
|
2019-10-02 12:28:17 +02:00
|
|
|
ItemSelectionChangedEvent
|
2019-04-12 14:24:34 +02:00
|
|
|
} from "./Item";
|
2019-02-18 17:23:57 +01:00
|
|
|
import StaticGraph, { staticGraphPropsDecoder } from "./items/StaticGraph";
|
|
|
|
import Icon, { iconPropsDecoder } from "./items/Icon";
|
|
|
|
import ColorCloud, { colorCloudPropsDecoder } from "./items/ColorCloud";
|
2020-10-09 12:45:10 +02:00
|
|
|
import NetworkLink, { networkLinkPropsDecoder } from "./items/NetworkLink";
|
2019-02-18 17:23:57 +01:00
|
|
|
import Group, { groupPropsDecoder } from "./items/Group";
|
2019-02-26 17:05:30 +01:00
|
|
|
import Clock, { clockPropsDecoder } from "./items/Clock";
|
2019-03-08 13:32:56 +01:00
|
|
|
import Box, { boxPropsDecoder } from "./items/Box";
|
2019-08-14 14:24:06 +02:00
|
|
|
import Line, { linePropsDecoder, LineMovedEvent } from "./items/Line";
|
2019-03-27 13:37:00 +01:00
|
|
|
import Label, { labelPropsDecoder } from "./items/Label";
|
|
|
|
import SimpleValue, { simpleValuePropsDecoder } from "./items/SimpleValue";
|
2019-04-01 11:50:30 +02:00
|
|
|
import EventsHistory, {
|
|
|
|
eventsHistoryPropsDecoder
|
|
|
|
} from "./items/EventsHistory";
|
2019-04-02 13:29:16 +02:00
|
|
|
import Percentile, { percentilePropsDecoder } from "./items/Percentile";
|
2019-06-04 13:17:19 +02:00
|
|
|
import TypedEvent, { Disposable, Listener } from "./lib/TypedEvent";
|
2019-04-10 18:33:05 +02:00
|
|
|
import DonutGraph, { donutGraphPropsDecoder } from "./items/DonutGraph";
|
|
|
|
import BarsGraph, { barsGraphPropsDecoder } from "./items/BarsGraph";
|
|
|
|
import ModuleGraph, { moduleGraphPropsDecoder } from "./items/ModuleGraph";
|
2019-04-15 18:14:18 +02:00
|
|
|
import Service, { servicePropsDecoder } from "./items/Service";
|
2019-09-27 15:44:39 +02:00
|
|
|
import { FormContainer } from "./Form";
|
2019-02-11 13:43:04 +01:00
|
|
|
|
2019-04-11 10:04:35 +02:00
|
|
|
// TODO: Document.
|
|
|
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
2019-06-04 13:17:19 +02:00
|
|
|
function itemInstanceFrom(data: AnyObject) {
|
2019-04-11 10:04:35 +02:00
|
|
|
const type = parseIntOr(data.type, null);
|
|
|
|
if (type == null) throw new TypeError("missing item type.");
|
|
|
|
|
2019-06-04 13:17:19 +02:00
|
|
|
const meta = itemMetaDecoder(data);
|
|
|
|
|
2019-04-11 10:04:35 +02:00
|
|
|
switch (type as ItemType) {
|
|
|
|
case ItemType.STATIC_GRAPH:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new StaticGraph(staticGraphPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.MODULE_GRAPH:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new ModuleGraph(moduleGraphPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.SIMPLE_VALUE:
|
|
|
|
case ItemType.SIMPLE_VALUE_MAX:
|
|
|
|
case ItemType.SIMPLE_VALUE_MIN:
|
|
|
|
case ItemType.SIMPLE_VALUE_AVG:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new SimpleValue(simpleValuePropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.PERCENTILE_BAR:
|
|
|
|
case ItemType.PERCENTILE_BUBBLE:
|
|
|
|
case ItemType.CIRCULAR_PROGRESS_BAR:
|
|
|
|
case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new Percentile(percentilePropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.LABEL:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new Label(labelPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.ICON:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new Icon(iconPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.SERVICE:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new Service(servicePropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.GROUP_ITEM:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new Group(groupPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.BOX_ITEM:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new Box(boxPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.LINE_ITEM:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new Line(linePropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.AUTO_SLA_GRAPH:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new EventsHistory(eventsHistoryPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.DONUT_GRAPH:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new DonutGraph(donutGraphPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.BARS_GRAPH:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new BarsGraph(barsGraphPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.CLOCK:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new Clock(clockPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.COLOR_CLOUD:
|
2019-06-04 13:17:19 +02:00
|
|
|
return new ColorCloud(colorCloudPropsDecoder(data), meta);
|
2020-10-09 12:45:10 +02:00
|
|
|
case ItemType.NETWORK_LINK:
|
|
|
|
return new NetworkLink(networkLinkPropsDecoder(data), meta);
|
2019-04-11 10:04:35 +02:00
|
|
|
default:
|
|
|
|
throw new TypeError("item not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Document.
|
|
|
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
2019-06-04 13:17:19 +02:00
|
|
|
function decodeProps(data: AnyObject) {
|
2019-04-11 10:04:35 +02:00
|
|
|
const type = parseIntOr(data.type, null);
|
|
|
|
if (type == null) throw new TypeError("missing item type.");
|
|
|
|
|
|
|
|
switch (type as ItemType) {
|
|
|
|
case ItemType.STATIC_GRAPH:
|
|
|
|
return staticGraphPropsDecoder(data);
|
|
|
|
case ItemType.MODULE_GRAPH:
|
2019-04-16 14:02:37 +02:00
|
|
|
return moduleGraphPropsDecoder(data);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.SIMPLE_VALUE:
|
|
|
|
case ItemType.SIMPLE_VALUE_MAX:
|
|
|
|
case ItemType.SIMPLE_VALUE_MIN:
|
|
|
|
case ItemType.SIMPLE_VALUE_AVG:
|
|
|
|
return simpleValuePropsDecoder(data);
|
|
|
|
case ItemType.PERCENTILE_BAR:
|
|
|
|
case ItemType.PERCENTILE_BUBBLE:
|
|
|
|
case ItemType.CIRCULAR_PROGRESS_BAR:
|
|
|
|
case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:
|
|
|
|
return percentilePropsDecoder(data);
|
|
|
|
case ItemType.LABEL:
|
|
|
|
return labelPropsDecoder(data);
|
|
|
|
case ItemType.ICON:
|
|
|
|
return iconPropsDecoder(data);
|
|
|
|
case ItemType.SERVICE:
|
2019-04-16 14:02:37 +02:00
|
|
|
return servicePropsDecoder(data);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.GROUP_ITEM:
|
|
|
|
return groupPropsDecoder(data);
|
|
|
|
case ItemType.BOX_ITEM:
|
|
|
|
return boxPropsDecoder(data);
|
|
|
|
case ItemType.LINE_ITEM:
|
|
|
|
return linePropsDecoder(data);
|
|
|
|
case ItemType.AUTO_SLA_GRAPH:
|
|
|
|
return eventsHistoryPropsDecoder(data);
|
|
|
|
case ItemType.DONUT_GRAPH:
|
2019-04-16 14:02:37 +02:00
|
|
|
return donutGraphPropsDecoder(data);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.BARS_GRAPH:
|
2019-04-16 14:02:37 +02:00
|
|
|
return barsGraphPropsDecoder(data);
|
2019-04-11 10:04:35 +02:00
|
|
|
case ItemType.CLOCK:
|
|
|
|
return clockPropsDecoder(data);
|
|
|
|
case ItemType.COLOR_CLOUD:
|
|
|
|
return colorCloudPropsDecoder(data);
|
2020-10-09 12:45:10 +02:00
|
|
|
case ItemType.NETWORK_LINK:
|
|
|
|
return networkLinkPropsDecoder(data);
|
2019-04-11 10:04:35 +02:00
|
|
|
default:
|
|
|
|
throw new TypeError("decoder not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-18 17:23:57 +01:00
|
|
|
// Base properties.
|
|
|
|
export interface VisualConsoleProps extends Size {
|
|
|
|
readonly id: number;
|
|
|
|
name: string;
|
|
|
|
groupId: number;
|
|
|
|
backgroundURL: string | null; // URL?
|
|
|
|
backgroundColor: string | null;
|
|
|
|
isFavorite: boolean;
|
2019-04-16 12:23:03 +02:00
|
|
|
relationLineWidth: number;
|
2019-02-18 17:23:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build a valid typed object from a raw object.
|
|
|
|
* This will allow us to ensure the type safety.
|
|
|
|
*
|
|
|
|
* @param data Raw object.
|
|
|
|
* @return An object representing the Visual Console props.
|
|
|
|
* @throws Will throw a TypeError if some property
|
|
|
|
* is missing from the raw object or have an invalid type.
|
|
|
|
*/
|
|
|
|
export function visualConsolePropsDecoder(
|
2019-06-04 13:17:19 +02:00
|
|
|
data: AnyObject
|
2019-02-18 17:23:57 +01:00
|
|
|
): VisualConsoleProps | never {
|
|
|
|
// Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation
|
|
|
|
const {
|
|
|
|
id,
|
|
|
|
name,
|
|
|
|
groupId,
|
|
|
|
backgroundURL,
|
|
|
|
backgroundColor,
|
2019-04-16 12:23:03 +02:00
|
|
|
isFavorite,
|
|
|
|
relationLineWidth
|
2019-02-18 17:23:57 +01:00
|
|
|
} = data;
|
|
|
|
|
|
|
|
if (id == null || isNaN(parseInt(id))) {
|
|
|
|
throw new TypeError("invalid Id.");
|
|
|
|
}
|
|
|
|
if (typeof name !== "string" || name.length === 0) {
|
|
|
|
throw new TypeError("invalid name.");
|
|
|
|
}
|
|
|
|
if (groupId == null || isNaN(parseInt(groupId))) {
|
|
|
|
throw new TypeError("invalid group Id.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
id: parseInt(id),
|
|
|
|
name,
|
|
|
|
groupId: parseInt(groupId),
|
2019-03-08 13:32:56 +01:00
|
|
|
backgroundURL: notEmptyStringOr(backgroundURL, null),
|
|
|
|
backgroundColor: notEmptyStringOr(backgroundColor, null),
|
2019-02-18 17:23:57 +01:00
|
|
|
isFavorite: parseBoolean(isFavorite),
|
2019-04-16 12:23:03 +02:00
|
|
|
relationLineWidth: parseIntOr(relationLineWidth, 0),
|
2019-02-18 17:23:57 +01:00
|
|
|
...sizePropsDecoder(data)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export default class VisualConsole {
|
|
|
|
// Reference to the DOM element which will contain the items.
|
|
|
|
private readonly containerRef: HTMLElement;
|
|
|
|
// Properties.
|
|
|
|
private _props: VisualConsoleProps;
|
2019-04-08 16:38:02 +02:00
|
|
|
// Visual Console Item instances by their Id.
|
|
|
|
private elementsById: {
|
2019-04-11 10:04:35 +02:00
|
|
|
[key: number]: Item<ItemProps>;
|
2019-04-08 16:38:02 +02:00
|
|
|
} = {};
|
|
|
|
// Visual Console Item Ids.
|
|
|
|
private elementIds: ItemProps["id"][] = [];
|
|
|
|
// Dictionary which store the created lines.
|
|
|
|
private relations: {
|
2019-04-11 10:04:35 +02:00
|
|
|
[key: string]: Line;
|
2019-04-08 16:38:02 +02:00
|
|
|
} = {};
|
2019-04-09 18:21:49 +02:00
|
|
|
// Event manager for click events.
|
2019-07-12 14:00:24 +02:00
|
|
|
private readonly clickEventManager = new TypedEvent<ItemClickEvent>();
|
|
|
|
// Event manager for double click events.
|
|
|
|
private readonly dblClickEventManager = new TypedEvent<ItemClickEvent>();
|
2019-06-11 16:44:19 +02:00
|
|
|
// Event manager for move events.
|
|
|
|
private readonly movedEventManager = new TypedEvent<ItemMovedEvent>();
|
2019-08-14 14:24:06 +02:00
|
|
|
// Event manager for line move events.
|
|
|
|
private readonly lineMovedEventManager = new TypedEvent<LineMovedEvent>();
|
2019-06-17 12:22:55 +02:00
|
|
|
// Event manager for resize events.
|
|
|
|
private readonly resizedEventManager = new TypedEvent<ItemResizedEvent>();
|
2019-07-25 17:58:37 +02:00
|
|
|
// Event manager for remove events.
|
2019-07-30 11:20:03 +02:00
|
|
|
private readonly selectionChangedEventManager = new TypedEvent<
|
|
|
|
ItemSelectionChangedEvent
|
|
|
|
>();
|
2019-04-09 18:21:49 +02:00
|
|
|
// List of references to clean the event listeners.
|
|
|
|
private readonly disposables: Disposable[] = [];
|
2019-02-18 17:23:57 +01:00
|
|
|
|
2019-04-12 14:24:34 +02:00
|
|
|
/**
|
|
|
|
* React to a click on an element.
|
|
|
|
* @param e Event object.
|
|
|
|
*/
|
2019-07-12 14:00:24 +02:00
|
|
|
private handleElementClick: (e: ItemClickEvent) => void = e => {
|
2019-04-12 14:24:34 +02:00
|
|
|
this.clickEventManager.emit(e);
|
|
|
|
// console.log(`Clicked element #${e.data.id}`, e);
|
|
|
|
};
|
|
|
|
|
2019-07-12 14:00:24 +02:00
|
|
|
/**
|
|
|
|
* React to a double click on an element.
|
|
|
|
* @param e Event object.
|
|
|
|
*/
|
|
|
|
private handleElementDblClick: (e: ItemClickEvent) => void = e => {
|
|
|
|
this.dblClickEventManager.emit(e);
|
|
|
|
// console.log(`Double clicked element #${e.data.id}`, e);
|
|
|
|
};
|
|
|
|
|
2019-06-11 16:44:19 +02:00
|
|
|
/**
|
|
|
|
* React to a movement on an element.
|
|
|
|
* @param e Event object.
|
|
|
|
*/
|
|
|
|
private handleElementMovement: (e: ItemMovedEvent) => void = e => {
|
2019-06-28 13:44:22 +02:00
|
|
|
// Move their relation lines.
|
|
|
|
const itemId = e.item.props.id;
|
|
|
|
const relations = this.getItemRelations(itemId);
|
|
|
|
|
|
|
|
relations.forEach(relation => {
|
|
|
|
if (relation.parentId === itemId) {
|
|
|
|
// Move the line start.
|
|
|
|
relation.line.props = {
|
|
|
|
...relation.line.props,
|
|
|
|
startPosition: this.getVisualCenter(e.newPosition, e.item)
|
|
|
|
};
|
|
|
|
} else if (relation.childId === itemId) {
|
|
|
|
// Move the line end.
|
|
|
|
relation.line.props = {
|
|
|
|
...relation.line.props,
|
|
|
|
endPosition: this.getVisualCenter(e.newPosition, e.item)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-06-11 16:44:19 +02:00
|
|
|
// console.log(`Moved element #${e.item.props.id}`, e);
|
|
|
|
};
|
|
|
|
|
2019-08-14 11:52:19 +02:00
|
|
|
/**
|
|
|
|
* React to a movement finished on an element.
|
|
|
|
* @param e Event object.
|
|
|
|
*/
|
|
|
|
private handleElementMovementFinished: (e: ItemMovedEvent) => void = e => {
|
|
|
|
this.movedEventManager.emit(e);
|
|
|
|
// console.log(`Movement finished for element #${e.item.props.id}`, e);
|
|
|
|
};
|
|
|
|
|
2019-08-14 14:24:06 +02:00
|
|
|
/**
|
|
|
|
* React to a line movement.
|
|
|
|
* @param e Event object.
|
|
|
|
*/
|
|
|
|
private handleLineElementMovementFinished: (
|
|
|
|
e: LineMovedEvent
|
|
|
|
) => void = e => {
|
|
|
|
this.lineMovedEventManager.emit(e);
|
|
|
|
// console.log(`Movement finished for element #${e.item.props.id}`, e);
|
|
|
|
};
|
|
|
|
|
2019-06-17 12:22:55 +02:00
|
|
|
/**
|
|
|
|
* React to a resizement on an element.
|
|
|
|
* @param e Event object.
|
|
|
|
*/
|
|
|
|
private handleElementResizement: (e: ItemResizedEvent) => void = e => {
|
2019-08-14 11:52:19 +02:00
|
|
|
// Move their relation lines.
|
|
|
|
const item = e.item;
|
|
|
|
const props = item.props;
|
|
|
|
const itemId = props.id;
|
|
|
|
const relations = this.getItemRelations(itemId);
|
|
|
|
|
|
|
|
const position = {
|
|
|
|
x: props.x,
|
|
|
|
y: props.y
|
|
|
|
};
|
|
|
|
|
2020-01-14 15:52:47 +01:00
|
|
|
const meta = this.elementsById[itemId].meta;
|
|
|
|
|
|
|
|
this.elementsById[itemId].meta = {
|
|
|
|
...meta,
|
|
|
|
isUpdating: true
|
|
|
|
};
|
|
|
|
|
2019-08-14 11:52:19 +02:00
|
|
|
relations.forEach(relation => {
|
|
|
|
if (relation.parentId === itemId) {
|
|
|
|
// Move the line start.
|
|
|
|
relation.line.props = {
|
|
|
|
...relation.line.props,
|
|
|
|
startPosition: this.getVisualCenter(position, item)
|
|
|
|
};
|
|
|
|
} else if (relation.childId === itemId) {
|
|
|
|
// Move the line end.
|
|
|
|
relation.line.props = {
|
|
|
|
...relation.line.props,
|
|
|
|
endPosition: this.getVisualCenter(position, item)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-06-17 12:22:55 +02:00
|
|
|
// console.log(`Resized element #${e.item.props.id}`, e);
|
|
|
|
};
|
|
|
|
|
2019-08-14 11:52:19 +02:00
|
|
|
/**
|
|
|
|
* React to a finished resizement on an element.
|
|
|
|
* @param e Event object.
|
|
|
|
*/
|
|
|
|
private handleElementResizementFinished: (
|
|
|
|
e: ItemResizedEvent
|
|
|
|
) => void = e => {
|
|
|
|
this.resizedEventManager.emit(e);
|
|
|
|
// console.log(`Resize fonished for element #${e.item.props.id}`, e);
|
|
|
|
};
|
|
|
|
|
2019-04-12 14:24:34 +02:00
|
|
|
/**
|
|
|
|
* Clear some element references.
|
|
|
|
* @param e Event object.
|
|
|
|
*/
|
2019-07-25 17:58:37 +02:00
|
|
|
private handleElementRemove: (e: ItemRemoveEvent) => void = e => {
|
2019-04-12 14:24:34 +02:00
|
|
|
// Remove the element from the list and its relations.
|
2019-07-25 17:58:37 +02:00
|
|
|
this.elementIds = this.elementIds.filter(id => id !== e.item.props.id);
|
|
|
|
delete this.elementsById[e.item.props.id];
|
|
|
|
this.clearRelations(e.item.props.id);
|
2019-07-30 11:20:03 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* React to element selection change
|
|
|
|
* @param e Event object.
|
|
|
|
*/
|
|
|
|
private handleElementSelectionChanged: (
|
|
|
|
e: ItemSelectionChangedEvent
|
|
|
|
) => void = e => {
|
|
|
|
if (this.elements.filter(item => item.meta.isSelected == true).length > 0) {
|
|
|
|
e.selected = true;
|
|
|
|
} else {
|
|
|
|
e.selected = false;
|
|
|
|
}
|
|
|
|
this.selectionChangedEventManager.emit(e);
|
2019-04-12 14:24:34 +02:00
|
|
|
};
|
|
|
|
|
2019-07-16 16:27:23 +02:00
|
|
|
// TODO: Document
|
|
|
|
private handleContainerClick: (e: MouseEvent) => void = () => {
|
2020-01-19 22:57:31 +01:00
|
|
|
this.unSelectItems();
|
2019-07-16 16:27:23 +02:00
|
|
|
};
|
|
|
|
|
2019-02-26 17:05:30 +01:00
|
|
|
public constructor(
|
2019-02-18 17:23:57 +01:00
|
|
|
container: HTMLElement,
|
2019-06-04 13:17:19 +02:00
|
|
|
props: AnyObject,
|
|
|
|
items: AnyObject[]
|
2019-02-18 17:23:57 +01:00
|
|
|
) {
|
|
|
|
this.containerRef = container;
|
2019-03-05 16:18:52 +01:00
|
|
|
this._props = visualConsolePropsDecoder(props);
|
2019-02-18 17:23:57 +01:00
|
|
|
|
|
|
|
// Force the first render.
|
|
|
|
this.render();
|
|
|
|
|
2019-08-09 15:19:35 +02:00
|
|
|
// Sort by id ASC
|
2019-04-08 16:38:02 +02:00
|
|
|
items = items.sort(function(a, b) {
|
2019-08-09 15:19:35 +02:00
|
|
|
if (a.id == null || b.id == null) return 0;
|
2019-04-25 16:36:17 +02:00
|
|
|
else if (a.id > b.id) return 1;
|
2019-04-08 16:38:02 +02:00
|
|
|
else return -1;
|
|
|
|
});
|
|
|
|
|
|
|
|
// Initialize the items.
|
2019-07-29 11:33:11 +02:00
|
|
|
items.forEach(item => this.addElement(item, this));
|
2019-02-18 17:23:57 +01:00
|
|
|
|
2019-04-08 16:38:02 +02:00
|
|
|
// Create lines.
|
|
|
|
this.buildRelations();
|
2019-07-16 16:27:23 +02:00
|
|
|
|
|
|
|
this.containerRef.addEventListener("click", this.handleContainerClick);
|
2019-04-08 16:38:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public accessor of the `elements` property.
|
|
|
|
* @return Properties.
|
|
|
|
*/
|
|
|
|
public get elements(): Item<ItemProps>[] {
|
|
|
|
// Ensure the type cause Typescript doesn't know the filter removes null items.
|
|
|
|
return this.elementIds
|
|
|
|
.map(id => this.elementsById[id])
|
|
|
|
.filter(_ => _ != null) as Item<ItemProps>[];
|
2019-02-18 17:23:57 +01:00
|
|
|
}
|
|
|
|
|
2019-07-29 11:33:11 +02:00
|
|
|
/**
|
|
|
|
* To create a new element add it to the DOM.
|
|
|
|
* @param item. Raw representation of the item's data.
|
|
|
|
*/
|
2019-07-31 13:23:25 +02:00
|
|
|
public addElement(item: AnyObject, context: this = this) {
|
2019-07-29 11:33:11 +02:00
|
|
|
try {
|
|
|
|
const itemInstance = itemInstanceFrom(item);
|
|
|
|
// Add the item to the list.
|
|
|
|
context.elementsById[itemInstance.props.id] = itemInstance;
|
|
|
|
context.elementIds.push(itemInstance.props.id);
|
|
|
|
// Item event handlers.
|
|
|
|
itemInstance.onRemove(context.handleElementRemove);
|
2019-08-08 11:05:21 +02:00
|
|
|
itemInstance.onSelectionChanged(context.handleElementSelectionChanged);
|
2019-07-30 11:20:03 +02:00
|
|
|
|
2020-01-19 22:57:31 +01:00
|
|
|
// TODO:Continue
|
|
|
|
itemInstance.onClick(context.handleElementClick);
|
|
|
|
itemInstance.onDblClick(context.handleElementDblClick);
|
|
|
|
itemInstance.onMoved(context.handleElementMovement);
|
|
|
|
itemInstance.onMovementFinished(context.handleElementMovementFinished);
|
2019-08-14 14:24:06 +02:00
|
|
|
if (itemInstance instanceof Line) {
|
|
|
|
itemInstance.onLineMovementFinished(
|
|
|
|
context.handleLineElementMovementFinished
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
itemInstance.onResized(context.handleElementResizement);
|
|
|
|
itemInstance.onResizeFinished(context.handleElementResizementFinished);
|
|
|
|
}
|
|
|
|
|
2019-07-29 11:33:11 +02:00
|
|
|
// Add the item to the DOM.
|
|
|
|
context.containerRef.append(itemInstance.elementRef);
|
2019-07-31 13:23:25 +02:00
|
|
|
return itemInstance;
|
2019-07-29 11:33:11 +02:00
|
|
|
} catch (error) {
|
|
|
|
console.log("Error creating a new element:", error.message);
|
|
|
|
}
|
2019-07-31 13:23:25 +02:00
|
|
|
return;
|
2019-07-29 11:33:11 +02:00
|
|
|
}
|
|
|
|
|
2019-04-11 10:04:35 +02:00
|
|
|
/**
|
|
|
|
* Public setter of the `elements` property.
|
|
|
|
* @param items.
|
|
|
|
*/
|
2019-06-04 13:17:19 +02:00
|
|
|
public updateElements(items: AnyObject[]): void {
|
|
|
|
// Ensure the type cause Typescript doesn't know the filter removes null items.
|
|
|
|
const itemIds = items
|
|
|
|
.map(item => item.id || null)
|
|
|
|
.filter(id => id != null) as number[];
|
2019-04-11 10:04:35 +02:00
|
|
|
// Get the elements we should delete.
|
2019-06-04 13:17:19 +02:00
|
|
|
const deletedIds = this.elementIds.filter(id => itemIds.indexOf(id) < 0);
|
2019-04-11 10:04:35 +02:00
|
|
|
// Delete the elements.
|
|
|
|
deletedIds.forEach(id => {
|
|
|
|
if (this.elementsById[id] != null) {
|
|
|
|
this.elementsById[id].remove();
|
|
|
|
delete this.elementsById[id];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// Replace the element ids.
|
|
|
|
this.elementIds = itemIds;
|
|
|
|
|
|
|
|
// Initialize the items.
|
|
|
|
items.forEach(item => {
|
|
|
|
if (item.id) {
|
|
|
|
if (this.elementsById[item.id] == null) {
|
|
|
|
// New item.
|
2019-07-29 11:33:11 +02:00
|
|
|
this.addElement(item);
|
2019-04-11 10:04:35 +02:00
|
|
|
} else {
|
|
|
|
// Update item.
|
|
|
|
try {
|
|
|
|
this.elementsById[item.id].props = decodeProps(item);
|
|
|
|
} catch (error) {
|
|
|
|
console.log("Error updating an element:", error.message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2019-04-12 14:24:34 +02:00
|
|
|
|
|
|
|
// Re-build relations.
|
|
|
|
this.buildRelations();
|
2019-04-11 10:04:35 +02:00
|
|
|
}
|
|
|
|
|
2019-07-08 17:51:01 +02:00
|
|
|
/**
|
|
|
|
* Public setter of the `element` property.
|
|
|
|
* @param item.
|
|
|
|
*/
|
|
|
|
public updateElement(item: AnyObject): void {
|
|
|
|
// Update item.
|
|
|
|
try {
|
|
|
|
this.elementsById[item.id].props = decodeProps(item);
|
|
|
|
} catch (error) {
|
|
|
|
console.log("Error updating element:", error.message);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Re-build relations.
|
|
|
|
this.buildRelations();
|
|
|
|
}
|
|
|
|
|
2019-02-18 17:23:57 +01:00
|
|
|
/**
|
|
|
|
* Public accessor of the `props` property.
|
|
|
|
* @return Properties.
|
|
|
|
*/
|
2019-02-26 17:05:30 +01:00
|
|
|
public get props(): VisualConsoleProps {
|
2019-04-11 10:04:35 +02:00
|
|
|
return { ...this._props }; // Return a copy.
|
2019-02-18 17:23:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public setter of the `props` property.
|
|
|
|
* If the new props are different enough than the
|
|
|
|
* stored props, a render would be fired.
|
|
|
|
* @param newProps
|
|
|
|
*/
|
2019-02-26 17:05:30 +01:00
|
|
|
public set props(newProps: VisualConsoleProps) {
|
2019-02-18 17:23:57 +01:00
|
|
|
const prevProps = this.props;
|
|
|
|
// Update the internal props.
|
|
|
|
this._props = newProps;
|
|
|
|
|
|
|
|
// From this point, things which rely on this.props can access to the changes.
|
|
|
|
|
|
|
|
// Re-render.
|
|
|
|
this.render(prevProps);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recreate or update the HTMLElement which represents the Visual Console into the DOM.
|
|
|
|
* @param prevProps If exists it will be used to only DOM updates instead of a full replace.
|
|
|
|
*/
|
2019-02-26 17:05:30 +01:00
|
|
|
public render(prevProps: VisualConsoleProps | null = null): void {
|
2019-02-18 17:23:57 +01:00
|
|
|
if (prevProps) {
|
|
|
|
if (prevProps.backgroundURL !== this.props.backgroundURL) {
|
2019-04-17 11:51:32 +02:00
|
|
|
this.containerRef.style.backgroundImage =
|
|
|
|
this.props.backgroundURL !== null
|
|
|
|
? `url(${this.props.backgroundURL})`
|
|
|
|
: null;
|
2019-02-18 17:23:57 +01:00
|
|
|
}
|
|
|
|
if (prevProps.backgroundColor !== this.props.backgroundColor) {
|
|
|
|
this.containerRef.style.backgroundColor = this.props.backgroundColor;
|
|
|
|
}
|
|
|
|
if (this.sizeChanged(prevProps, this.props)) {
|
|
|
|
this.resizeElement(this.props.width, this.props.height);
|
|
|
|
}
|
|
|
|
} else {
|
2019-04-17 11:51:32 +02:00
|
|
|
this.containerRef.style.backgroundImage =
|
|
|
|
this.props.backgroundURL !== null
|
|
|
|
? `url(${this.props.backgroundURL})`
|
|
|
|
: null;
|
|
|
|
|
2019-02-18 17:23:57 +01:00
|
|
|
this.containerRef.style.backgroundColor = this.props.backgroundColor;
|
|
|
|
this.resizeElement(this.props.width, this.props.height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare the previous and the new size and return
|
|
|
|
* a boolean value in case the size changed.
|
|
|
|
* @param prevSize
|
|
|
|
* @param newSize
|
|
|
|
* @return Whether the size changed or not.
|
|
|
|
*/
|
2019-02-26 17:05:30 +01:00
|
|
|
public sizeChanged(prevSize: Size, newSize: Size): boolean {
|
2019-02-18 17:23:57 +01:00
|
|
|
return (
|
|
|
|
prevSize.width !== newSize.width || prevSize.height !== newSize.height
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resize the DOM container.
|
|
|
|
* @param width
|
|
|
|
* @param height
|
|
|
|
*/
|
2019-02-26 17:05:30 +01:00
|
|
|
public resizeElement(width: number, height: number): void {
|
2019-02-18 17:23:57 +01:00
|
|
|
this.containerRef.style.width = `${width}px`;
|
|
|
|
this.containerRef.style.height = `${height}px`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the size into the properties and resize the DOM container.
|
|
|
|
* @param width
|
|
|
|
* @param height
|
|
|
|
*/
|
2019-02-26 17:05:30 +01:00
|
|
|
public resize(width: number, height: number): void {
|
2019-02-18 17:23:57 +01:00
|
|
|
this.props = {
|
|
|
|
...this.props, // Object spread: http://es6-features.org/#SpreadOperator
|
|
|
|
width,
|
|
|
|
height
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* To remove the event listeners and the elements from the DOM.
|
|
|
|
*/
|
2019-02-26 17:05:30 +01:00
|
|
|
public remove(): void {
|
2019-04-09 18:21:49 +02:00
|
|
|
this.disposables.forEach(d => d.dispose()); // Arrow function.
|
2019-02-18 17:23:57 +01:00
|
|
|
this.elements.forEach(e => e.remove()); // Arrow function.
|
2019-04-08 16:38:02 +02:00
|
|
|
this.elementsById = {};
|
|
|
|
this.elementIds = [];
|
2019-04-12 14:24:34 +02:00
|
|
|
// Clear relations.
|
|
|
|
this.clearRelations();
|
2019-07-29 11:33:11 +02:00
|
|
|
// Remove the click event listener.
|
|
|
|
this.containerRef.removeEventListener("click", this.handleContainerClick);
|
2019-04-01 11:50:30 +02:00
|
|
|
// Clean container.
|
|
|
|
this.containerRef.innerHTML = "";
|
2019-02-18 17:23:57 +01:00
|
|
|
}
|
2019-04-08 16:38:02 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create line elements which connect the elements with their parents.
|
|
|
|
*/
|
2019-07-29 11:33:11 +02:00
|
|
|
public buildRelations(): void {
|
2019-04-12 14:24:34 +02:00
|
|
|
// Clear relations.
|
|
|
|
this.clearRelations();
|
|
|
|
// Add relations.
|
2019-04-08 16:38:02 +02:00
|
|
|
this.elements.forEach(item => {
|
|
|
|
if (item.props.parentId !== null) {
|
|
|
|
const parent = this.elementsById[item.props.parentId];
|
|
|
|
const child = this.elementsById[item.props.id];
|
|
|
|
if (parent && child) this.addRelationLine(parent, child);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-12 14:24:34 +02:00
|
|
|
/**
|
|
|
|
* @param itemId Optional identifier of a parent or child item.
|
|
|
|
* Remove the line elements which connect the elements with their parents.
|
|
|
|
*/
|
|
|
|
private clearRelations(itemId?: number): void {
|
|
|
|
if (itemId != null) {
|
|
|
|
for (let key in this.relations) {
|
|
|
|
const ids = key.split("|");
|
|
|
|
const parentId = Number.parseInt(ids[0]);
|
|
|
|
const childId = Number.parseInt(ids[1]);
|
|
|
|
|
|
|
|
if (itemId === parentId || itemId === childId) {
|
|
|
|
this.relations[key].remove();
|
|
|
|
delete this.relations[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (let key in this.relations) {
|
|
|
|
this.relations[key].remove();
|
|
|
|
delete this.relations[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 16:38:02 +02:00
|
|
|
/**
|
|
|
|
* Retrieve the line element which represent the relation between items.
|
|
|
|
* @param parentId Identifier of the parent item.
|
|
|
|
* @param childId Itentifier of the child item.
|
|
|
|
* @return The line element or nothing.
|
|
|
|
*/
|
|
|
|
private getRelationLine(parentId: number, childId: number): Line | null {
|
|
|
|
const identifier = `${parentId}|${childId}`;
|
|
|
|
return this.relations[identifier] || null;
|
|
|
|
}
|
|
|
|
|
2019-06-28 13:44:22 +02:00
|
|
|
// TODO: Document.
|
|
|
|
private getItemRelations(
|
|
|
|
itemId: number
|
|
|
|
): {
|
|
|
|
parentId: number;
|
|
|
|
childId: number;
|
|
|
|
line: Line;
|
|
|
|
}[] {
|
|
|
|
const itemRelations = [];
|
|
|
|
|
|
|
|
for (let key in this.relations) {
|
|
|
|
const ids = key.split("|");
|
|
|
|
const parentId = Number.parseInt(ids[0]);
|
|
|
|
const childId = Number.parseInt(ids[1]);
|
|
|
|
|
|
|
|
if (itemId === parentId || itemId === childId) {
|
|
|
|
itemRelations.push({
|
|
|
|
parentId,
|
|
|
|
childId,
|
|
|
|
line: this.relations[key]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return itemRelations;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the visual center of the item. It's ussually the center of the
|
|
|
|
* content, like the label doesn't exist.
|
|
|
|
* @param position Initial position.
|
|
|
|
* @param element Element we want to use.
|
|
|
|
*/
|
|
|
|
private getVisualCenter(
|
|
|
|
position: Position,
|
|
|
|
element: Item<ItemProps>
|
|
|
|
): Position {
|
2019-07-02 17:32:46 +02:00
|
|
|
let x = position.x + element.elementRef.clientWidth / 2;
|
|
|
|
let y = position.y + element.elementRef.clientHeight / 2;
|
|
|
|
if (
|
|
|
|
typeof element.props.label !== "undefined" ||
|
|
|
|
element.props.label !== "" ||
|
|
|
|
element.props.label !== null
|
|
|
|
) {
|
|
|
|
switch (element.props.labelPosition) {
|
|
|
|
case "up":
|
|
|
|
y =
|
|
|
|
position.y +
|
|
|
|
(element.elementRef.clientHeight +
|
|
|
|
element.labelElementRef.clientHeight) /
|
|
|
|
2;
|
|
|
|
break;
|
|
|
|
case "down":
|
|
|
|
y =
|
|
|
|
position.y +
|
|
|
|
(element.elementRef.clientHeight -
|
|
|
|
element.labelElementRef.clientHeight) /
|
|
|
|
2;
|
|
|
|
break;
|
|
|
|
case "right":
|
|
|
|
x =
|
|
|
|
position.x +
|
|
|
|
(element.elementRef.clientWidth -
|
|
|
|
element.labelElementRef.clientWidth) /
|
|
|
|
2;
|
|
|
|
break;
|
|
|
|
case "left":
|
|
|
|
x =
|
|
|
|
position.x +
|
|
|
|
(element.elementRef.clientWidth +
|
|
|
|
element.labelElementRef.clientWidth) /
|
|
|
|
2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-06-28 13:44:22 +02:00
|
|
|
return { x, y };
|
|
|
|
}
|
|
|
|
|
2019-04-08 16:38:02 +02:00
|
|
|
/**
|
|
|
|
* Add a new line item to represent a relation between the items.
|
|
|
|
* @param parent Parent item.
|
|
|
|
* @param child Child item.
|
|
|
|
* @return Whether the line was added or not.
|
|
|
|
*/
|
|
|
|
private addRelationLine(
|
|
|
|
parent: Item<ItemProps>,
|
|
|
|
child: Item<ItemProps>
|
|
|
|
): Line {
|
|
|
|
const identifier = `${parent.props.id}|${child.props.id}`;
|
|
|
|
if (this.relations[identifier] != null) {
|
2019-04-11 10:04:35 +02:00
|
|
|
this.relations[identifier].remove();
|
2019-04-08 16:38:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get the items center.
|
2019-06-28 13:44:22 +02:00
|
|
|
const { x: startX, y: startY } = this.getVisualCenter(parent.props, parent);
|
|
|
|
const { x: endX, y: endY } = this.getVisualCenter(child.props, child);
|
2019-04-08 16:38:02 +02:00
|
|
|
|
|
|
|
const line = new Line(
|
|
|
|
linePropsDecoder({
|
|
|
|
id: 0,
|
|
|
|
type: ItemType.LINE_ITEM,
|
|
|
|
startX,
|
|
|
|
startY,
|
|
|
|
endX,
|
|
|
|
endY,
|
|
|
|
width: 0,
|
2019-04-16 12:23:03 +02:00
|
|
|
height: 0,
|
2019-04-16 14:02:37 +02:00
|
|
|
lineWidth: this.props.relationLineWidth,
|
|
|
|
color: "#CCCCCC"
|
2019-06-04 13:17:19 +02:00
|
|
|
}),
|
|
|
|
itemMetaDecoder({
|
|
|
|
receivedAt: new Date()
|
2019-04-08 16:38:02 +02:00
|
|
|
})
|
|
|
|
);
|
|
|
|
// Save a reference to the line item.
|
|
|
|
this.relations[identifier] = line;
|
|
|
|
|
|
|
|
// Add the line to the DOM.
|
|
|
|
line.elementRef.style.zIndex = "0";
|
|
|
|
this.containerRef.append(line.elementRef);
|
|
|
|
|
|
|
|
return line;
|
|
|
|
}
|
2019-04-09 18:21:49 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Add an event handler to the click of the linked visual console elements.
|
|
|
|
* @param listener Function which is going to be executed when a linked console is clicked.
|
|
|
|
*/
|
2019-07-12 14:00:24 +02:00
|
|
|
public onItemClick(listener: Listener<ItemClickEvent>): Disposable {
|
2019-04-09 18:21:49 +02:00
|
|
|
/*
|
|
|
|
* 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.clickEventManager.on(listener);
|
|
|
|
this.disposables.push(disposable);
|
|
|
|
|
|
|
|
return disposable;
|
|
|
|
}
|
2019-06-04 17:21:54 +02:00
|
|
|
|
2019-07-12 14:00:24 +02:00
|
|
|
/**
|
|
|
|
* Add an event handler to the double click of the linked visual console elements.
|
|
|
|
* @param listener Function which is going to be executed when a linked console is double clicked.
|
|
|
|
*/
|
|
|
|
public onItemDblClick(listener: Listener<ItemClickEvent>): 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.dblClickEventManager.on(listener);
|
|
|
|
this.disposables.push(disposable);
|
|
|
|
|
|
|
|
return disposable;
|
|
|
|
}
|
|
|
|
|
2019-06-11 16:44:19 +02:00
|
|
|
/**
|
|
|
|
* Add an event handler to the movement of the visual console elements.
|
|
|
|
* @param listener Function which is going to be executed when a linked console is moved.
|
|
|
|
*/
|
|
|
|
public onItemMoved(listener: Listener<ItemMovedEvent>): 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.movedEventManager.on(listener);
|
|
|
|
this.disposables.push(disposable);
|
|
|
|
|
|
|
|
return disposable;
|
|
|
|
}
|
|
|
|
|
2019-08-14 14:24:06 +02:00
|
|
|
/**
|
|
|
|
* Add an event handler to the movement of the visual console line elements.
|
|
|
|
* @param listener Function which is going to be executed when a linked console is moved.
|
|
|
|
*/
|
|
|
|
public onLineMoved(listener: Listener<LineMovedEvent>): 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.disposables.push(disposable);
|
|
|
|
|
|
|
|
return disposable;
|
|
|
|
}
|
|
|
|
|
2019-06-17 12:22:55 +02:00
|
|
|
/**
|
|
|
|
* Add an event handler to the resizement of the visual console elements.
|
|
|
|
* @param listener Function which is going to be executed when a linked console is moved.
|
|
|
|
*/
|
|
|
|
public onItemResized(listener: Listener<ItemResizedEvent>): 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.resizedEventManager.on(listener);
|
|
|
|
this.disposables.push(disposable);
|
|
|
|
|
|
|
|
return disposable;
|
|
|
|
}
|
|
|
|
|
2019-07-25 17:58:37 +02:00
|
|
|
/**
|
2019-07-30 11:20:03 +02:00
|
|
|
* Add an event handler to the elements selection change of the visual console .
|
2019-07-25 17:58:37 +02:00
|
|
|
* @param listener Function which is going to be executed when a linked console is moved.
|
|
|
|
*/
|
2019-07-30 11:20:03 +02:00
|
|
|
public onItemSelectionChanged(
|
|
|
|
listener: Listener<ItemSelectionChangedEvent>
|
|
|
|
): Disposable {
|
2019-07-25 17:58:37 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2019-07-30 11:20:03 +02:00
|
|
|
const disposable = this.selectionChangedEventManager.on(listener);
|
2019-07-25 17:58:37 +02:00
|
|
|
this.disposables.push(disposable);
|
|
|
|
|
|
|
|
return disposable;
|
|
|
|
}
|
|
|
|
|
2019-06-04 17:21:54 +02:00
|
|
|
/**
|
|
|
|
* Enable the edition mode.
|
|
|
|
*/
|
|
|
|
public enableEditMode(): void {
|
|
|
|
this.elements.forEach(item => {
|
|
|
|
item.meta = { ...item.meta, editMode: true };
|
|
|
|
});
|
2019-06-11 15:19:27 +02:00
|
|
|
this.containerRef.classList.add("is-editing");
|
2019-06-04 17:21:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disable the edition mode.
|
|
|
|
*/
|
|
|
|
public disableEditMode(): void {
|
|
|
|
this.elements.forEach(item => {
|
|
|
|
item.meta = { ...item.meta, editMode: false };
|
|
|
|
});
|
2019-06-11 15:19:27 +02:00
|
|
|
this.containerRef.classList.remove("is-editing");
|
2019-06-04 17:21:54 +02:00
|
|
|
}
|
2019-07-12 14:00:24 +02:00
|
|
|
|
2020-01-21 12:55:54 +01:00
|
|
|
/**
|
|
|
|
* Select an item.
|
|
|
|
* @param itemId Item Id.
|
|
|
|
* @param unique To remove the selection of other items or not.
|
|
|
|
*/
|
|
|
|
public selectItem(itemId: number, unique: boolean = false): void {
|
|
|
|
if (unique) {
|
|
|
|
this.elementIds.forEach(currentItemId => {
|
|
|
|
const meta = this.elementsById[currentItemId].meta;
|
|
|
|
|
|
|
|
if (currentItemId !== itemId && meta.isSelected) {
|
|
|
|
this.elementsById[currentItemId].unSelectItem();
|
|
|
|
} else if (currentItemId === itemId && !meta.isSelected) {
|
|
|
|
this.elementsById[currentItemId].selectItem();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else if (this.elementsById[itemId]) {
|
|
|
|
this.elementsById[itemId].selectItem();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unselect an item.
|
|
|
|
* @param itemId Item Id.
|
|
|
|
*/
|
|
|
|
public unSelectItem(itemId: number): void {
|
|
|
|
if (this.elementsById[itemId]) {
|
|
|
|
const meta = this.elementsById[itemId].meta;
|
|
|
|
|
|
|
|
if (meta.isSelected) {
|
|
|
|
this.elementsById[itemId].unSelectItem();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-12 14:00:24 +02:00
|
|
|
/**
|
|
|
|
* Unselect all items.
|
|
|
|
*/
|
2020-01-19 22:57:31 +01:00
|
|
|
public unSelectItems(): void {
|
2019-07-12 14:00:24 +02:00
|
|
|
this.elementIds.forEach(itemId => {
|
|
|
|
if (this.elementsById[itemId]) {
|
2020-01-19 22:57:31 +01:00
|
|
|
this.elementsById[itemId].unSelectItem();
|
2019-07-12 14:00:24 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2019-07-29 11:33:11 +02:00
|
|
|
|
|
|
|
// TODO: Document.
|
|
|
|
public static items = {
|
|
|
|
[ItemType.STATIC_GRAPH]: StaticGraph,
|
|
|
|
[ItemType.MODULE_GRAPH]: ModuleGraph,
|
|
|
|
[ItemType.SIMPLE_VALUE]: SimpleValue,
|
|
|
|
[ItemType.SIMPLE_VALUE_MAX]: SimpleValue,
|
|
|
|
[ItemType.SIMPLE_VALUE_MIN]: SimpleValue,
|
|
|
|
[ItemType.SIMPLE_VALUE_AVG]: SimpleValue,
|
|
|
|
[ItemType.PERCENTILE_BAR]: Percentile,
|
|
|
|
[ItemType.PERCENTILE_BUBBLE]: Percentile,
|
|
|
|
[ItemType.CIRCULAR_PROGRESS_BAR]: Percentile,
|
|
|
|
[ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR]: Percentile,
|
|
|
|
[ItemType.LABEL]: Label,
|
|
|
|
[ItemType.ICON]: Icon,
|
|
|
|
[ItemType.SERVICE]: Service,
|
|
|
|
[ItemType.GROUP_ITEM]: Group,
|
|
|
|
[ItemType.BOX_ITEM]: Box,
|
|
|
|
[ItemType.LINE_ITEM]: Line,
|
|
|
|
[ItemType.AUTO_SLA_GRAPH]: EventsHistory,
|
|
|
|
[ItemType.DONUT_GRAPH]: DonutGraph,
|
|
|
|
[ItemType.BARS_GRAPH]: BarsGraph,
|
|
|
|
[ItemType.CLOCK]: Clock,
|
2020-10-09 12:45:10 +02:00
|
|
|
[ItemType.COLOR_CLOUD]: ColorCloud,
|
|
|
|
[ItemType.NETWORK_LINK]: NetworkLink
|
2019-07-29 11:33:11 +02:00
|
|
|
};
|
2019-07-30 14:58:01 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Relying type item and srcimg and agent and module
|
|
|
|
* name convert name item representative.
|
|
|
|
*
|
|
|
|
* @param item Instance item from extract name.
|
|
|
|
*
|
|
|
|
* @return Name item.
|
|
|
|
*/
|
|
|
|
public static itemDescriptiveName(item: Item<ItemProps>): string {
|
|
|
|
let text: string;
|
|
|
|
switch (item.props.type) {
|
|
|
|
case ItemType.STATIC_GRAPH:
|
|
|
|
text = `${t("Static graph")} - ${(item as StaticGraph).props.imageSrc}`;
|
|
|
|
break;
|
|
|
|
case ItemType.MODULE_GRAPH:
|
|
|
|
text = t("Module graph");
|
|
|
|
break;
|
|
|
|
case ItemType.CLOCK:
|
|
|
|
text = t("Clock");
|
|
|
|
break;
|
|
|
|
case ItemType.BARS_GRAPH:
|
|
|
|
text = t("Bars graph");
|
|
|
|
break;
|
|
|
|
case ItemType.AUTO_SLA_GRAPH:
|
2020-01-23 19:54:47 +01:00
|
|
|
text = t("Event history graph");
|
2019-07-30 14:58:01 +02:00
|
|
|
break;
|
|
|
|
case ItemType.PERCENTILE_BAR:
|
|
|
|
text = t("Percentile bar");
|
|
|
|
break;
|
|
|
|
case ItemType.CIRCULAR_PROGRESS_BAR:
|
|
|
|
text = t("Circular progress bar");
|
|
|
|
break;
|
|
|
|
case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:
|
|
|
|
text = t("Circular progress bar (interior)");
|
|
|
|
break;
|
|
|
|
case ItemType.SIMPLE_VALUE:
|
|
|
|
text = t("Simple Value");
|
|
|
|
break;
|
|
|
|
case ItemType.LABEL:
|
|
|
|
text = t("Label");
|
|
|
|
break;
|
|
|
|
case ItemType.GROUP_ITEM:
|
|
|
|
text = t("Group");
|
|
|
|
break;
|
|
|
|
case ItemType.COLOR_CLOUD:
|
|
|
|
text = t("Color cloud");
|
|
|
|
break;
|
|
|
|
case ItemType.ICON:
|
|
|
|
text = `${t("Icon")} - ${(item as Icon).props.imageSrc}`;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
text = t("Item");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const linkedAgentAndModuleProps = item.props as Partial<WithModuleProps>;
|
|
|
|
if (
|
|
|
|
linkedAgentAndModuleProps.agentAlias != null &&
|
|
|
|
linkedAgentAndModuleProps.moduleName != null
|
|
|
|
) {
|
|
|
|
text += ` (${ellipsize(
|
|
|
|
linkedAgentAndModuleProps.agentAlias,
|
|
|
|
18
|
|
|
|
)} - ${ellipsize(linkedAgentAndModuleProps.moduleName, 25)})`;
|
|
|
|
} else if (linkedAgentAndModuleProps.agentAlias != null) {
|
|
|
|
text += ` (${ellipsize(linkedAgentAndModuleProps.agentAlias, 25)})`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
2019-02-18 17:23:57 +01:00
|
|
|
}
|