1423 lines
41 KiB
TypeScript
1423 lines
41 KiB
TypeScript
import { AnyObject, Size, Position, WithModuleProps } from "./lib/types";
|
|
import {
|
|
parseBoolean,
|
|
sizePropsDecoder,
|
|
parseIntOr,
|
|
notEmptyStringOr,
|
|
itemMetaDecoder,
|
|
t,
|
|
ellipsize,
|
|
debounce
|
|
} from "./lib";
|
|
import Item, {
|
|
ItemType,
|
|
ItemProps,
|
|
ItemClickEvent,
|
|
ItemRemoveEvent,
|
|
ItemMovedEvent,
|
|
ItemResizedEvent,
|
|
ItemSelectionChangedEvent
|
|
} from "./Item";
|
|
import StaticGraph, { staticGraphPropsDecoder } from "./items/StaticGraph";
|
|
import Icon, { iconPropsDecoder } from "./items/Icon";
|
|
import ColorCloud, { colorCloudPropsDecoder } from "./items/ColorCloud";
|
|
import NetworkLink, { networkLinkPropsDecoder } from "./items/NetworkLink";
|
|
import Group, { groupPropsDecoder } from "./items/Group";
|
|
import Clock, { clockPropsDecoder } from "./items/Clock";
|
|
import Box, { boxPropsDecoder } from "./items/Box";
|
|
import Line, { linePropsDecoder, LineMovedEvent } from "./items/Line";
|
|
import Label, { labelPropsDecoder } from "./items/Label";
|
|
import SimpleValue, { simpleValuePropsDecoder } from "./items/SimpleValue";
|
|
import EventsHistory, {
|
|
eventsHistoryPropsDecoder
|
|
} from "./items/EventsHistory";
|
|
import Percentile, { percentilePropsDecoder } from "./items/Percentile";
|
|
import TypedEvent, { Disposable, Listener } from "./lib/TypedEvent";
|
|
import DonutGraph, { donutGraphPropsDecoder } from "./items/DonutGraph";
|
|
import BarsGraph, { barsGraphPropsDecoder } from "./items/BarsGraph";
|
|
import ModuleGraph, { moduleGraphPropsDecoder } from "./items/ModuleGraph";
|
|
import Service, { servicePropsDecoder } from "./items/Service";
|
|
import Odometer, { odometerPropsDecoder } from "./items/Odometer";
|
|
import BasicChart, { basicChartPropsDecoder } from "./items/BasicChart";
|
|
|
|
// TODO: Document.
|
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
function itemInstanceFrom(data: AnyObject) {
|
|
const type = parseIntOr(data.type, null);
|
|
if (type == null) throw new TypeError("missing item type.");
|
|
|
|
const meta = itemMetaDecoder(data);
|
|
|
|
switch (type as ItemType) {
|
|
case ItemType.STATIC_GRAPH:
|
|
return new StaticGraph(staticGraphPropsDecoder(data), meta);
|
|
case ItemType.MODULE_GRAPH:
|
|
return new ModuleGraph(moduleGraphPropsDecoder(data), meta);
|
|
case ItemType.SIMPLE_VALUE:
|
|
case ItemType.SIMPLE_VALUE_MAX:
|
|
case ItemType.SIMPLE_VALUE_MIN:
|
|
case ItemType.SIMPLE_VALUE_AVG:
|
|
return new SimpleValue(simpleValuePropsDecoder(data), meta);
|
|
case ItemType.PERCENTILE_BAR:
|
|
case ItemType.PERCENTILE_BUBBLE:
|
|
case ItemType.CIRCULAR_PROGRESS_BAR:
|
|
case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:
|
|
return new Percentile(percentilePropsDecoder(data), meta);
|
|
case ItemType.LABEL:
|
|
return new Label(labelPropsDecoder(data), meta);
|
|
case ItemType.ICON:
|
|
return new Icon(iconPropsDecoder(data), meta);
|
|
case ItemType.SERVICE:
|
|
return new Service(servicePropsDecoder(data), meta);
|
|
case ItemType.GROUP_ITEM:
|
|
return new Group(groupPropsDecoder(data), meta);
|
|
case ItemType.BOX_ITEM:
|
|
return new Box(boxPropsDecoder(data), meta);
|
|
case ItemType.LINE_ITEM:
|
|
return new Line(linePropsDecoder(data), meta);
|
|
case ItemType.AUTO_SLA_GRAPH:
|
|
return new EventsHistory(eventsHistoryPropsDecoder(data), meta);
|
|
case ItemType.DONUT_GRAPH:
|
|
return new DonutGraph(donutGraphPropsDecoder(data), meta);
|
|
case ItemType.BARS_GRAPH:
|
|
return new BarsGraph(barsGraphPropsDecoder(data), meta);
|
|
case ItemType.CLOCK:
|
|
return new Clock(clockPropsDecoder(data), meta);
|
|
case ItemType.COLOR_CLOUD:
|
|
return new ColorCloud(colorCloudPropsDecoder(data), meta);
|
|
case ItemType.NETWORK_LINK:
|
|
return new NetworkLink(networkLinkPropsDecoder(data), meta);
|
|
case ItemType.ODOMETER:
|
|
return new Odometer(odometerPropsDecoder(data), meta);
|
|
case ItemType.BASIC_CHART:
|
|
return new BasicChart(basicChartPropsDecoder(data), meta);
|
|
default:
|
|
throw new TypeError("item not found");
|
|
}
|
|
}
|
|
|
|
// TODO: Document.
|
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
function decodeProps(data: AnyObject) {
|
|
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:
|
|
return moduleGraphPropsDecoder(data);
|
|
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:
|
|
return servicePropsDecoder(data);
|
|
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:
|
|
return donutGraphPropsDecoder(data);
|
|
case ItemType.BARS_GRAPH:
|
|
return barsGraphPropsDecoder(data);
|
|
case ItemType.CLOCK:
|
|
return clockPropsDecoder(data);
|
|
case ItemType.COLOR_CLOUD:
|
|
return colorCloudPropsDecoder(data);
|
|
case ItemType.NETWORK_LINK:
|
|
return networkLinkPropsDecoder(data);
|
|
case ItemType.ODOMETER:
|
|
return odometerPropsDecoder(data);
|
|
case ItemType.BASIC_CHART:
|
|
return basicChartPropsDecoder(data);
|
|
default:
|
|
throw new TypeError("decoder not found");
|
|
}
|
|
}
|
|
|
|
// Base properties.
|
|
export interface VisualConsoleProps extends Size {
|
|
readonly id: number;
|
|
name: string;
|
|
groupId: number;
|
|
backgroundURL: string | null; // URL?
|
|
backgroundColor: string | null;
|
|
isFavorite: boolean;
|
|
relationLineWidth: number;
|
|
maintenanceMode: MaintenanceModeInterface | null;
|
|
}
|
|
|
|
export interface MaintenanceModeInterface {
|
|
user: string;
|
|
timestamp: number;
|
|
}
|
|
|
|
/**
|
|
* 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(
|
|
data: AnyObject
|
|
): VisualConsoleProps | never {
|
|
// Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation
|
|
const {
|
|
id,
|
|
name,
|
|
groupId,
|
|
backgroundURL,
|
|
backgroundColor,
|
|
isFavorite,
|
|
relationLineWidth,
|
|
maintenanceMode
|
|
} = 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),
|
|
backgroundURL: notEmptyStringOr(backgroundURL, null),
|
|
backgroundColor: notEmptyStringOr(backgroundColor, null),
|
|
isFavorite: parseBoolean(isFavorite),
|
|
relationLineWidth: parseIntOr(relationLineWidth, 0),
|
|
maintenanceMode: maintenanceMode,
|
|
...sizePropsDecoder(data)
|
|
};
|
|
}
|
|
|
|
export default class VisualConsole {
|
|
// Reference to the DOM element which will contain the items.
|
|
private readonly containerRef: HTMLElement;
|
|
// Properties.
|
|
private _props: VisualConsoleProps;
|
|
// Visual Console Item instances by their Id.
|
|
private elementsById: {
|
|
[key: number]: Item<ItemProps>;
|
|
} = {};
|
|
// Visual Console Item Ids.
|
|
private elementIds: ItemProps["id"][] = [];
|
|
// Dictionary which store the created lines.
|
|
private relations: {
|
|
[key: string]: Line;
|
|
} = {};
|
|
|
|
// Dictionary which store the related items (by ID).
|
|
private lineLinks: {
|
|
[key: number]: { [key: number]: { [key: string]: number } };
|
|
} = {};
|
|
|
|
private lines: {
|
|
[key: number]: { [key: string]: number };
|
|
} = {};
|
|
|
|
// Event manager for click events.
|
|
private readonly clickEventManager = new TypedEvent<ItemClickEvent>();
|
|
// Event manager for double click events.
|
|
private readonly dblClickEventManager = new TypedEvent<ItemClickEvent>();
|
|
// Event manager for move events.
|
|
private readonly movedEventManager = new TypedEvent<ItemMovedEvent>();
|
|
// Event manager for line move events.
|
|
private readonly lineMovedEventManager = new TypedEvent<LineMovedEvent>();
|
|
// Event manager for resize events.
|
|
private readonly resizedEventManager = new TypedEvent<ItemResizedEvent>();
|
|
// Event manager for remove events.
|
|
private readonly selectionChangedEventManager = new TypedEvent<
|
|
ItemSelectionChangedEvent
|
|
>();
|
|
// List of references to clean the event listeners.
|
|
private readonly disposables: Disposable[] = [];
|
|
|
|
/**
|
|
* React to a click on an element.
|
|
* @param e Event object.
|
|
*/
|
|
private handleElementClick: (e: ItemClickEvent) => void = e => {
|
|
this.clickEventManager.emit(e);
|
|
// console.log(`Clicked element #${e.data.id}`, e);
|
|
};
|
|
|
|
/**
|
|
* 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);
|
|
};
|
|
|
|
/**
|
|
* React to a movement on an element.
|
|
* @param e Event object.
|
|
*/
|
|
private handleElementMovement: (e: ItemMovedEvent) => void = e => {
|
|
// 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)
|
|
};
|
|
}
|
|
});
|
|
|
|
// Move lines conneted with this item.
|
|
this.updateLinesConnected(e.item.props, e.newPosition, false);
|
|
|
|
// console.log(`Moved element #${e.item.props.id}`, e);
|
|
};
|
|
|
|
/**
|
|
* React to a movement finished on an element.
|
|
* @param e Event object.
|
|
*/
|
|
private handleElementMovementFinished: (e: ItemMovedEvent) => void = e => {
|
|
this.movedEventManager.emit(e);
|
|
// Move lines conneted with this item.
|
|
this.updateLinesConnected(e.item.props, e.newPosition, true);
|
|
// console.log(`Movement finished for element #${e.item.props.id}`, e);
|
|
};
|
|
|
|
/**
|
|
* Verifies if x,y are inside item coordinates.
|
|
* @param x Coordinate X
|
|
* @param y Coordinate Y
|
|
* @param item ItemProps instance.
|
|
*/
|
|
private coordinatesInItem(x: number, y: number, props: ItemProps) {
|
|
if (
|
|
props.type == ItemType.LINE_ITEM ||
|
|
props.type == ItemType.NETWORK_LINK
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
x > props.x &&
|
|
x < props.x + props.width &&
|
|
y > props.y &&
|
|
y < props.y + props.height
|
|
) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* React to a line movement.
|
|
* @param e Event object.
|
|
*/
|
|
private handleLineElementMovementFinished: (
|
|
e: LineMovedEvent
|
|
) => void = e => {
|
|
// Update links.
|
|
this.refreshLink(e.item);
|
|
|
|
// Build line relationships between items and lines.
|
|
this.lineMovedEventManager.emit(e);
|
|
|
|
// console.log(`Movement finished for element #${e.item.props.id}`, e);
|
|
};
|
|
|
|
/**
|
|
* React to a resizement on an element.
|
|
* @param e Event object.
|
|
*/
|
|
private handleElementResizement: (e: ItemResizedEvent) => void = e => {
|
|
// 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
|
|
};
|
|
|
|
const meta = this.elementsById[itemId].meta;
|
|
|
|
this.elementsById[itemId].meta = {
|
|
...meta,
|
|
isUpdating: true
|
|
};
|
|
|
|
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)
|
|
};
|
|
}
|
|
});
|
|
|
|
// console.log(`Resized element #${e.item.props.id}`, e);
|
|
};
|
|
|
|
/**
|
|
* 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);
|
|
};
|
|
|
|
/**
|
|
* Clear some element references.
|
|
* @param e Event object.
|
|
*/
|
|
private handleElementRemove: (e: ItemRemoveEvent) => void = e => {
|
|
// Remove the element from the list and its relations.
|
|
this.elementIds = this.elementIds.filter(id => id !== e.item.props.id);
|
|
delete this.elementsById[e.item.props.id];
|
|
this.clearRelations(e.item.props.id);
|
|
};
|
|
|
|
/**
|
|
* 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);
|
|
};
|
|
|
|
// TODO: Document
|
|
private handleContainerClick: (e: MouseEvent) => void = () => {
|
|
this.unSelectItems();
|
|
};
|
|
|
|
/**
|
|
* Refresh link for given line.
|
|
*
|
|
* @param line Line.
|
|
*/
|
|
protected refreshLink(l: Line) {
|
|
let line: number = l.props.id;
|
|
let itemAtStart = 0;
|
|
let itemAtEnd = 0;
|
|
|
|
try {
|
|
for (let i in this.elementsById) {
|
|
if (
|
|
this.coordinatesInItem(
|
|
l.props.startPosition.x,
|
|
l.props.startPosition.y,
|
|
this.elementsById[i].props
|
|
)
|
|
) {
|
|
// Start position at element i.
|
|
itemAtStart = parseInt(i);
|
|
}
|
|
|
|
if (
|
|
this.coordinatesInItem(
|
|
l.props.endPosition.x,
|
|
l.props.endPosition.y,
|
|
this.elementsById[i].props
|
|
)
|
|
) {
|
|
// Start position at element i.
|
|
itemAtEnd = parseInt(i);
|
|
}
|
|
}
|
|
|
|
if (this.lineLinks == null) {
|
|
this.lineLinks = {};
|
|
}
|
|
|
|
if (this.lines == null) {
|
|
this.lines = {};
|
|
}
|
|
|
|
if (itemAtStart == line) {
|
|
itemAtStart = 0;
|
|
}
|
|
|
|
if (itemAtEnd == line) {
|
|
itemAtEnd = 0;
|
|
}
|
|
|
|
// Initialize line if not registered.
|
|
if (this.lines[line] == null) {
|
|
this.lines[line] = {
|
|
start: itemAtStart,
|
|
end: itemAtEnd
|
|
};
|
|
}
|
|
|
|
// Register 'start' side of the line.
|
|
if (itemAtStart > 0) {
|
|
// Initialize.
|
|
if (this.lineLinks[itemAtStart] == null) {
|
|
this.lineLinks[itemAtStart] = {};
|
|
}
|
|
|
|
// Assign.
|
|
this.lineLinks[itemAtStart][line] = {
|
|
start: itemAtStart,
|
|
end: itemAtEnd
|
|
};
|
|
|
|
// Register line if not exists prviously.
|
|
} else {
|
|
// Clean previous line relationship.
|
|
if (this.lines[line]["start"] > 0) {
|
|
this.lineLinks[this.lines[line]["start"]][line]["start"] = 0;
|
|
this.lines[line]["start"] = 0;
|
|
}
|
|
}
|
|
|
|
if (itemAtEnd > 0) {
|
|
if (this.lineLinks[itemAtEnd] == null) {
|
|
this.lineLinks[itemAtEnd] = {};
|
|
}
|
|
|
|
this.lineLinks[itemAtEnd][line] = {
|
|
start: itemAtStart,
|
|
end: itemAtEnd
|
|
};
|
|
} else {
|
|
// Clean previous line relationship.
|
|
if (this.lines[line]["end"] > 0) {
|
|
this.lineLinks[this.lines[line]["end"]][line]["end"] = 0;
|
|
this.lines[line]["end"] = 0;
|
|
}
|
|
}
|
|
|
|
this.lines[line] = {
|
|
start: itemAtStart,
|
|
end: itemAtEnd
|
|
};
|
|
|
|
// Cleanup.
|
|
for (let i in this.lineLinks) {
|
|
if (this.lineLinks[i][line]) {
|
|
if (
|
|
this.lineLinks[i][line].start == 0 &&
|
|
this.lineLinks[i][line].end == 0
|
|
) {
|
|
// Object not connected to a line.
|
|
delete this.lineLinks[i][line];
|
|
|
|
if (Object.keys(this.lineLinks[i]).length === 0) {
|
|
delete this.lineLinks[i];
|
|
}
|
|
}
|
|
|
|
if (
|
|
(this.lineLinks[i][line].start != itemAtStart &&
|
|
this.lineLinks[i][line].end == itemAtEnd) ||
|
|
(this.lineLinks[i][line].start == itemAtStart &&
|
|
this.lineLinks[i][line].end != itemAtEnd)
|
|
) {
|
|
// Object not connected to a line.
|
|
delete this.lineLinks[i][line];
|
|
|
|
if (Object.keys(this.lineLinks[i]).length === 0) {
|
|
delete this.lineLinks[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates lines connected to this item.
|
|
*
|
|
* @param item Item moved.
|
|
* @param newPosition New location for item.
|
|
* @param oldPosition Old location for item.
|
|
* @param save Save to ajax or not.
|
|
*/
|
|
protected updateLinesConnected(item: ItemProps, to: Position, save: boolean) {
|
|
if (this.lineLinks[item.id] == null) {
|
|
return;
|
|
}
|
|
|
|
Object.keys(this.lineLinks[item.id]).forEach(i => {
|
|
let lineId = parseInt(i);
|
|
const found = this.elementIds.indexOf(lineId);
|
|
if (found === -1) {
|
|
return;
|
|
}
|
|
let line = this.elementsById[lineId] as Line;
|
|
if (line.props) {
|
|
let startX = line.props.startPosition.x;
|
|
let startY = line.props.startPosition.y;
|
|
let endX = line.props.endPosition.x;
|
|
let endY = line.props.endPosition.y;
|
|
|
|
if (item.id == this.lineLinks[item.id][lineId]["start"]) {
|
|
startX = to.x + item.width / 2;
|
|
startY = to.y + item.height / 2;
|
|
}
|
|
|
|
if (item.id == this.lineLinks[item.id][lineId]["end"]) {
|
|
endX = to.x + item.width / 2;
|
|
endY = to.y + item.height / 2;
|
|
}
|
|
|
|
// Update line movement.
|
|
this.updateElement({
|
|
...line.props,
|
|
startX: startX,
|
|
startY: startY,
|
|
endX: endX,
|
|
endY: endY
|
|
});
|
|
|
|
if (save) {
|
|
let debouncedLinePositionSave = debounce(
|
|
500,
|
|
(options: AnyObject) => {
|
|
this.lineMovedEventManager.emit({
|
|
item: options.line,
|
|
startPosition: {
|
|
x: options.startX,
|
|
y: options.startY
|
|
},
|
|
endPosition: {
|
|
x: options.endX,
|
|
y: options.endY
|
|
}
|
|
});
|
|
}
|
|
);
|
|
|
|
// Save line positon.
|
|
debouncedLinePositionSave({
|
|
line: line,
|
|
startX: startX,
|
|
startY: startY,
|
|
endX: endX,
|
|
endY: endY
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
// Update parents...
|
|
this.buildRelations(item.id, to.x + item.width / 2, to.y + item.height / 2);
|
|
}
|
|
|
|
public constructor(
|
|
container: HTMLElement,
|
|
props: AnyObject,
|
|
items: AnyObject[]
|
|
) {
|
|
this.containerRef = container;
|
|
this._props = visualConsolePropsDecoder(props);
|
|
|
|
// Force the first render.
|
|
this.render();
|
|
|
|
// Sort by id ASC
|
|
items = items.sort(function(a, b) {
|
|
if (a.id == null || b.id == null) return 0;
|
|
else if (a.id > b.id) return 1;
|
|
else return -1;
|
|
});
|
|
|
|
// Initialize the items.
|
|
items.forEach(item => this.addElement(item, this));
|
|
|
|
// Create lines.
|
|
this.buildRelations();
|
|
|
|
// Re-attach all connected lines if any.
|
|
this.elements.forEach(item => {
|
|
if (item instanceof Line) {
|
|
this.refreshLink(item);
|
|
}
|
|
});
|
|
|
|
this.containerRef.addEventListener("click", this.handleContainerClick);
|
|
}
|
|
|
|
/**
|
|
* 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>[];
|
|
}
|
|
|
|
/**
|
|
* To create a new element add it to the DOM.
|
|
* @param item. Raw representation of the item's data.
|
|
*/
|
|
public addElement(item: AnyObject, context: this = this) {
|
|
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);
|
|
itemInstance.onSelectionChanged(context.handleElementSelectionChanged);
|
|
itemInstance.onClick(context.handleElementClick);
|
|
itemInstance.onDblClick(context.handleElementDblClick);
|
|
|
|
// TODO:Continue
|
|
if (itemInstance instanceof Line) {
|
|
itemInstance.onLineMovementFinished(
|
|
context.handleLineElementMovementFinished
|
|
);
|
|
this.refreshLink(itemInstance);
|
|
} else {
|
|
itemInstance.onMoved(context.handleElementMovement);
|
|
itemInstance.onMovementFinished(context.handleElementMovementFinished);
|
|
itemInstance.onResized(context.handleElementResizement);
|
|
itemInstance.onResizeFinished(context.handleElementResizementFinished);
|
|
}
|
|
|
|
// Add the item to the DOM.
|
|
context.containerRef.append(itemInstance.elementRef);
|
|
return itemInstance;
|
|
} catch (error) {
|
|
console.error("Error creating a new element:", (error as Error).message);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Public setter of the `elements` property.
|
|
* @param items.
|
|
*/
|
|
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[];
|
|
// Get the elements we should delete.
|
|
const deletedIds = this.elementIds.filter(id => itemIds.indexOf(id) < 0);
|
|
// 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.
|
|
this.addElement(item);
|
|
} else {
|
|
// Update item.
|
|
try {
|
|
this.elementsById[item.id].props = decodeProps(item);
|
|
} catch (error) {
|
|
console.error(
|
|
"Error updating an element:",
|
|
(error as Error).message
|
|
);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Re-build relations.
|
|
this.buildRelations();
|
|
}
|
|
|
|
/**
|
|
* 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.error("Error updating element:", (error as Error).message);
|
|
}
|
|
|
|
// Re-build relations.
|
|
this.buildRelations();
|
|
}
|
|
|
|
/**
|
|
* Public accessor of the `props` property.
|
|
* @return Properties.
|
|
*/
|
|
public get props(): VisualConsoleProps {
|
|
return { ...this._props }; // Return a copy.
|
|
}
|
|
|
|
/**
|
|
* Public setter of the `props` property.
|
|
* If the new props are different enough than the
|
|
* stored props, a render would be fired.
|
|
* @param newProps
|
|
*/
|
|
public set props(newProps: VisualConsoleProps) {
|
|
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.
|
|
*/
|
|
public render(prevProps: VisualConsoleProps | null = null): void {
|
|
if (prevProps) {
|
|
if (prevProps.backgroundURL !== this.props.backgroundURL) {
|
|
this.containerRef.style.backgroundImage =
|
|
this.props.backgroundURL !== null
|
|
? `url(${this.props.backgroundURL})`
|
|
: "";
|
|
}
|
|
if (this.props.backgroundColor != null)
|
|
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 {
|
|
if (this.props.backgroundURL)
|
|
this.containerRef.style.backgroundImage =
|
|
this.props.backgroundURL !== null
|
|
? `url(${this.props.backgroundURL})`
|
|
: "";
|
|
|
|
if (this.props.backgroundColor)
|
|
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.
|
|
*/
|
|
public sizeChanged(prevSize: Size, newSize: Size): boolean {
|
|
return (
|
|
prevSize.width !== newSize.width || prevSize.height !== newSize.height
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Resize the DOM container.
|
|
* @param width
|
|
* @param height
|
|
*/
|
|
public resizeElement(width: number, height: number): void {
|
|
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
|
|
*/
|
|
public resize(width: number, height: number): void {
|
|
this.props = {
|
|
...this.props, // Object spread: http://es6-features.org/#SpreadOperator
|
|
width,
|
|
height
|
|
};
|
|
}
|
|
|
|
/**
|
|
* To remove the event listeners and the elements from the DOM.
|
|
*/
|
|
public remove(): void {
|
|
this.disposables.forEach(d => d.dispose()); // Arrow function.
|
|
this.elements.forEach(e => e.remove()); // Arrow function.
|
|
this.elementsById = {};
|
|
this.elementIds = [];
|
|
// Clear relations.
|
|
this.clearRelations();
|
|
// Remove the click event listener.
|
|
this.containerRef.removeEventListener("click", this.handleContainerClick);
|
|
// Clean container.
|
|
this.containerRef.innerHTML = "";
|
|
}
|
|
|
|
/**
|
|
* Create line elements which connect the elements with their parents.
|
|
*
|
|
* When itemId is being moved, overwrite position of the 'parent' or 'child'
|
|
* endpoints of the line, using X and Y values.
|
|
*/
|
|
public buildRelations(itemId?: number, x?: number, y?: number): void {
|
|
// Clear relations.
|
|
this.clearRelations();
|
|
// Add relations.
|
|
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) {
|
|
if (itemId != undefined) {
|
|
if (item.props.parentId == itemId) {
|
|
// Update parent line position.
|
|
this.addRelationLine(parent, child, x, y);
|
|
} else if (item.props.id == itemId) {
|
|
// Update child line position.
|
|
this.addRelationLine(parent, child, undefined, undefined, x, y);
|
|
} else {
|
|
this.addRelationLine(parent, child);
|
|
}
|
|
} else {
|
|
// No movements default behaviour.
|
|
this.addRelationLine(parent, child);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @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];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
// 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 {
|
|
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;
|
|
}
|
|
}
|
|
return { x, y };
|
|
}
|
|
|
|
/**
|
|
* 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>,
|
|
parentX?: number,
|
|
parentY?: number,
|
|
childX?: number,
|
|
childY?: number
|
|
): Line {
|
|
const identifier = `${parent.props.id}|${child.props.id}`;
|
|
if (this.relations[identifier] != null) {
|
|
this.relations[identifier].remove();
|
|
}
|
|
|
|
// Get the items center.
|
|
let { x: startX, y: startY } = this.getVisualCenter(parent.props, parent);
|
|
let { x: endX, y: endY } = this.getVisualCenter(child.props, child);
|
|
|
|
// Overwrite positions if needed (while moving it!).
|
|
if (parentX != null) {
|
|
startX = parentX;
|
|
}
|
|
|
|
if (parentY != null) {
|
|
startY = parentY;
|
|
}
|
|
|
|
if (childX != null) {
|
|
endX = childX;
|
|
}
|
|
|
|
if (childY != null) {
|
|
endY = childY;
|
|
}
|
|
|
|
// Line inherits child element status.
|
|
const line = new Line(
|
|
linePropsDecoder({
|
|
id: 0,
|
|
type: ItemType.LINE_ITEM,
|
|
startX,
|
|
startY,
|
|
endX,
|
|
endY,
|
|
width: 0,
|
|
height: 0,
|
|
lineWidth: this.props.relationLineWidth,
|
|
color: notEmptyStringOr(child.props.colorStatus, "#CCC")
|
|
}),
|
|
itemMetaDecoder({
|
|
receivedAt: new Date()
|
|
})
|
|
);
|
|
// 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;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public onItemClick(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.clickEventManager.on(listener);
|
|
this.disposables.push(disposable);
|
|
|
|
return disposable;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Add an event handler to the elements selection change of the visual console .
|
|
* @param listener Function which is going to be executed when a linked console is moved.
|
|
*/
|
|
public onItemSelectionChanged(
|
|
listener: Listener<ItemSelectionChangedEvent>
|
|
): 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.selectionChangedEventManager.on(listener);
|
|
this.disposables.push(disposable);
|
|
|
|
return disposable;
|
|
}
|
|
|
|
/**
|
|
* Enable the edition mode.
|
|
*/
|
|
public enableEditMode(): void {
|
|
this.elements.forEach(item => {
|
|
item.meta = { ...item.meta, editMode: true };
|
|
});
|
|
this.containerRef.classList.add("is-editing");
|
|
}
|
|
|
|
/**
|
|
* Disable the edition mode.
|
|
*/
|
|
public disableEditMode(): void {
|
|
this.elements.forEach(item => {
|
|
item.meta = { ...item.meta, editMode: false };
|
|
});
|
|
this.containerRef.classList.remove("is-editing");
|
|
}
|
|
|
|
/**
|
|
* Enable the maintenance mode.
|
|
*/
|
|
public enableMaintenanceMode(): void {
|
|
this.elements.forEach(item => {
|
|
item.meta = { ...item.meta, maintenanceMode: true };
|
|
});
|
|
this.containerRef.classList.add("is-maintenance");
|
|
this.containerRef.classList.remove("is-editing");
|
|
}
|
|
|
|
/**
|
|
* Disable the maintenance mode.
|
|
*/
|
|
public disableMaintenanceMode(): void {
|
|
this.elements.forEach(item => {
|
|
item.meta = { ...item.meta, maintenanceMode: false };
|
|
});
|
|
this.containerRef.classList.remove("is-maintenance");
|
|
this.containerRef.classList.add("is-editing");
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unselect all items.
|
|
*/
|
|
public unSelectItems(): void {
|
|
this.elementIds.forEach(itemId => {
|
|
if (this.elementsById[itemId]) {
|
|
this.elementsById[itemId].unSelectItem();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 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,
|
|
[ItemType.COLOR_CLOUD]: ColorCloud,
|
|
[ItemType.NETWORK_LINK]: NetworkLink,
|
|
[ItemType.ODOMETER]: Odometer,
|
|
[ItemType.BASIC_CHART]: BasicChart
|
|
};
|
|
|
|
/**
|
|
* 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:
|
|
text = t("Event history graph");
|
|
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;
|
|
case ItemType.ODOMETER:
|
|
text = t("Odometer");
|
|
break;
|
|
case ItemType.BASIC_CHART:
|
|
text = t("BasicChart");
|
|
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;
|
|
}
|
|
}
|