mirror of
https://github.com/pandorafms/pandorafms.git
synced 2025-07-31 01:35:36 +02:00
WIP: Visual console client
This commit is contained in:
parent
30491a307c
commit
5c5d40cfd4
@ -147,7 +147,7 @@ export class FormContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public removeInputGroup(inputGroupName: string): FormContainer {
|
public removeInputGroup(inputGroupName: string): FormContainer {
|
||||||
// delete this.inputGroupsByName[inputGroupName];
|
delete this.inputGroupsByName[inputGroupName];
|
||||||
// Remove the current stored name.
|
// Remove the current stored name.
|
||||||
this.enabledInputGroupNames = this.enabledInputGroupNames.filter(
|
this.enabledInputGroupNames = this.enabledInputGroupNames.filter(
|
||||||
name => name === inputGroupName
|
name => name === inputGroupName
|
||||||
|
@ -17,7 +17,8 @@ import {
|
|||||||
addMovementListener,
|
addMovementListener,
|
||||||
debounce,
|
debounce,
|
||||||
addResizementListener,
|
addResizementListener,
|
||||||
t
|
t,
|
||||||
|
helpTip
|
||||||
} from "./lib";
|
} from "./lib";
|
||||||
import TypedEvent, { Listener, Disposable } from "./lib/TypedEvent";
|
import TypedEvent, { Listener, Disposable } from "./lib/TypedEvent";
|
||||||
import { FormContainer, InputGroup } from "./Form";
|
import { FormContainer, InputGroup } from "./Form";
|
||||||
@ -84,7 +85,76 @@ export interface ItemResizedEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Document
|
// TODO: Document
|
||||||
class PositionInputGroup extends InputGroup<ItemProps> {
|
class LinkInputGroup extends InputGroup<Partial<ItemProps>> {
|
||||||
|
protected createContent(): HTMLElement | HTMLElement[] {
|
||||||
|
const linkLabel = document.createElement("label");
|
||||||
|
linkLabel.textContent = t("Link enabled");
|
||||||
|
|
||||||
|
const linkInputChkbx = document.createElement("input");
|
||||||
|
linkInputChkbx.id = "checkbox-switch";
|
||||||
|
linkInputChkbx.className = "checkbox-switch";
|
||||||
|
linkInputChkbx.type = "checkbox";
|
||||||
|
linkInputChkbx.name = "checkbox-enable-link";
|
||||||
|
linkInputChkbx.value = "1";
|
||||||
|
linkInputChkbx.checked =
|
||||||
|
this.currentData.isLinkEnabled || this.initialData.isLinkEnabled || false;
|
||||||
|
linkInputChkbx.addEventListener("change", e =>
|
||||||
|
this.updateData({
|
||||||
|
isLinkEnabled: (e.target as HTMLInputElement).checked
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const linkInputLabel = document.createElement("label");
|
||||||
|
linkInputLabel.className = "label-switch";
|
||||||
|
linkInputLabel.htmlFor = "checkbox-switch";
|
||||||
|
|
||||||
|
linkLabel.appendChild(linkInputChkbx);
|
||||||
|
linkLabel.appendChild(linkInputLabel);
|
||||||
|
|
||||||
|
return linkLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
class OnTopInputGroup extends InputGroup<Partial<ItemProps>> {
|
||||||
|
protected createContent(): HTMLElement | HTMLElement[] {
|
||||||
|
const onTopLabel = document.createElement("label");
|
||||||
|
onTopLabel.textContent = t("Show on top");
|
||||||
|
|
||||||
|
const onTopInputChkbx = document.createElement("input");
|
||||||
|
onTopInputChkbx.id = "checkbox-switch";
|
||||||
|
onTopInputChkbx.className = "checkbox-switch";
|
||||||
|
onTopInputChkbx.type = "checkbox";
|
||||||
|
onTopInputChkbx.name = "checkbox-show-on-top";
|
||||||
|
onTopInputChkbx.value = "1";
|
||||||
|
onTopInputChkbx.checked =
|
||||||
|
this.currentData.isOnTop || this.initialData.isOnTop || false;
|
||||||
|
onTopInputChkbx.addEventListener("change", e =>
|
||||||
|
this.updateData({
|
||||||
|
isOnTop: (e.target as HTMLInputElement).checked
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const onTopInputLabel = document.createElement("label");
|
||||||
|
onTopInputLabel.className = "label-switch";
|
||||||
|
onTopInputLabel.htmlFor = "checkbox-switch";
|
||||||
|
|
||||||
|
onTopLabel.appendChild(
|
||||||
|
helpTip(
|
||||||
|
t(
|
||||||
|
"It allows the element to be superimposed to the rest of items of the visual console"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
onTopLabel.appendChild(onTopInputChkbx);
|
||||||
|
onTopLabel.appendChild(onTopInputLabel);
|
||||||
|
|
||||||
|
return onTopLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
class PositionInputGroup extends InputGroup<Partial<ItemProps>> {
|
||||||
protected createContent(): HTMLElement | HTMLElement[] {
|
protected createContent(): HTMLElement | HTMLElement[] {
|
||||||
const positionLabel = document.createElement("label");
|
const positionLabel = document.createElement("label");
|
||||||
positionLabel.textContent = t("Position");
|
positionLabel.textContent = t("Position");
|
||||||
@ -118,8 +188,54 @@ class PositionInputGroup extends InputGroup<ItemProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
class SizeInputGroup extends InputGroup<Partial<ItemProps>> {
|
||||||
|
protected createContent(): HTMLElement | HTMLElement[] {
|
||||||
|
const sizeLabel = document.createElement("label");
|
||||||
|
sizeLabel.textContent = t("Size");
|
||||||
|
|
||||||
|
const sizeInputWidth = document.createElement("input");
|
||||||
|
sizeInputWidth.type = "number";
|
||||||
|
sizeInputWidth.min = "0";
|
||||||
|
sizeInputWidth.required = true;
|
||||||
|
sizeInputWidth.value = `${this.currentData.width ||
|
||||||
|
this.initialData.width ||
|
||||||
|
0}`;
|
||||||
|
sizeInputWidth.addEventListener("change", e =>
|
||||||
|
this.updateData({
|
||||||
|
width: parseIntOr((e.target as HTMLInputElement).value, 0)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const sizeInputHeight = document.createElement("input");
|
||||||
|
sizeInputHeight.type = "number";
|
||||||
|
sizeInputHeight.min = "0";
|
||||||
|
sizeInputHeight.required = true;
|
||||||
|
sizeInputHeight.value = `${this.currentData.height ||
|
||||||
|
this.initialData.height ||
|
||||||
|
0}`;
|
||||||
|
sizeInputHeight.addEventListener("change", e =>
|
||||||
|
this.updateData({
|
||||||
|
height: parseIntOr((e.target as HTMLInputElement).value, 0)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
sizeLabel.appendChild(
|
||||||
|
helpTip(
|
||||||
|
t(
|
||||||
|
"In order to use the original image file size, set width and height to 0."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
sizeLabel.appendChild(sizeInputWidth);
|
||||||
|
sizeLabel.appendChild(sizeInputHeight);
|
||||||
|
|
||||||
|
return sizeLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract a valid enum value from a raw label positi9on value.
|
* Extract a valid enum value from a raw label position value.
|
||||||
* @param labelPosition Raw value.
|
* @param labelPosition Raw value.
|
||||||
*/
|
*/
|
||||||
const parseLabelPosition = (
|
const parseLabelPosition = (
|
||||||
@ -380,7 +496,8 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||||||
this.childElementRef = this.createDomElement();
|
this.childElementRef = this.createDomElement();
|
||||||
|
|
||||||
// Insert the elements into the container.
|
// Insert the elements into the container.
|
||||||
this.elementRef.append(this.childElementRef, this.labelElementRef);
|
this.elementRef.appendChild(this.childElementRef);
|
||||||
|
this.elementRef.appendChild(this.labelElementRef);
|
||||||
|
|
||||||
// Resize element.
|
// Resize element.
|
||||||
this.resizeElement(this.itemProps.width, this.itemProps.height);
|
this.resizeElement(this.itemProps.width, this.itemProps.height);
|
||||||
@ -468,8 +585,10 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||||||
const cell = document.createElement("td");
|
const cell = document.createElement("td");
|
||||||
|
|
||||||
cell.innerHTML = label;
|
cell.innerHTML = label;
|
||||||
row.append(cell);
|
row.appendChild(cell);
|
||||||
table.append(emptyRow1, row, emptyRow2);
|
table.appendChild(emptyRow1);
|
||||||
|
table.appendChild(row);
|
||||||
|
table.appendChild(emptyRow2);
|
||||||
table.style.textAlign = "center";
|
table.style.textAlign = "center";
|
||||||
|
|
||||||
// Change the table size depending on its position.
|
// Change the table size depending on its position.
|
||||||
@ -491,7 +610,7 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// element.innerHTML = this.props.label;
|
// element.innerHTML = this.props.label;
|
||||||
element.append(table);
|
element.appendChild(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
@ -897,15 +1016,6 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Document
|
|
||||||
public getFormContainer(): FormContainer {
|
|
||||||
return new FormContainer(
|
|
||||||
t("Item"),
|
|
||||||
[new PositionInputGroup("position", this.props)],
|
|
||||||
["position"]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To add an event handler to the click of the linked visual console elements.
|
* To 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.
|
* @param listener Function which is going to be executed when a linked console is clicked.
|
||||||
@ -985,6 +1095,25 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||||||
|
|
||||||
return disposable;
|
return disposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
public getFormContainer(): FormContainer {
|
||||||
|
return VisualConsoleItem.getFormContainer(this.props);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
public static getFormContainer(props: Partial<ItemProps>): FormContainer {
|
||||||
|
return new FormContainer(
|
||||||
|
t("Item"),
|
||||||
|
[
|
||||||
|
new PositionInputGroup("position", props),
|
||||||
|
new SizeInputGroup("size", props),
|
||||||
|
new LinkInputGroup("link", props),
|
||||||
|
new OnTopInputGroup("show-on-top", props)
|
||||||
|
],
|
||||||
|
["position", "size", "link", "show-on-top"]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default VisualConsoleItem;
|
export default VisualConsoleItem;
|
||||||
|
@ -314,24 +314,7 @@ export default class VisualConsole {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Initialize the items.
|
// Initialize the items.
|
||||||
items.forEach(item => {
|
items.forEach(item => this.addElement(item, this));
|
||||||
try {
|
|
||||||
const itemInstance = itemInstanceFrom(item);
|
|
||||||
// Add the item to the list.
|
|
||||||
this.elementsById[itemInstance.props.id] = itemInstance;
|
|
||||||
this.elementIds.push(itemInstance.props.id);
|
|
||||||
// Item event handlers.
|
|
||||||
itemInstance.onClick(this.handleElementClick);
|
|
||||||
itemInstance.onDblClick(this.handleElementDblClick);
|
|
||||||
itemInstance.onMoved(this.handleElementMovement);
|
|
||||||
itemInstance.onResized(this.handleElementResizement);
|
|
||||||
itemInstance.onRemove(this.handleElementRemove);
|
|
||||||
// Add the item to the DOM.
|
|
||||||
this.containerRef.append(itemInstance.elementRef);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error creating a new element:", error.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create lines.
|
// Create lines.
|
||||||
this.buildRelations();
|
this.buildRelations();
|
||||||
@ -350,6 +333,29 @@ export default class VisualConsole {
|
|||||||
.filter(_ => _ != null) as Item<ItemProps>[];
|
.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): void {
|
||||||
|
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.onClick(context.handleElementClick);
|
||||||
|
itemInstance.onDblClick(context.handleElementDblClick);
|
||||||
|
itemInstance.onMoved(context.handleElementMovement);
|
||||||
|
itemInstance.onResized(context.handleElementResizement);
|
||||||
|
itemInstance.onRemove(context.handleElementRemove);
|
||||||
|
// Add the item to the DOM.
|
||||||
|
context.containerRef.append(itemInstance.elementRef);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error creating a new element:", error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public setter of the `elements` property.
|
* Public setter of the `elements` property.
|
||||||
* @param items.
|
* @param items.
|
||||||
@ -376,18 +382,7 @@ export default class VisualConsole {
|
|||||||
if (item.id) {
|
if (item.id) {
|
||||||
if (this.elementsById[item.id] == null) {
|
if (this.elementsById[item.id] == null) {
|
||||||
// New item.
|
// New item.
|
||||||
try {
|
this.addElement(item);
|
||||||
const itemInstance = itemInstanceFrom(item);
|
|
||||||
// Add the item to the list.
|
|
||||||
this.elementsById[itemInstance.props.id] = itemInstance;
|
|
||||||
// Item event handlers.
|
|
||||||
itemInstance.onClick(this.handleElementClick);
|
|
||||||
itemInstance.onRemove(this.handleElementRemove);
|
|
||||||
// Add the item to the DOM.
|
|
||||||
this.containerRef.append(itemInstance.elementRef);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error creating a new element:", error.message);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Update item.
|
// Update item.
|
||||||
try {
|
try {
|
||||||
@ -519,6 +514,8 @@ export default class VisualConsole {
|
|||||||
this.elementIds = [];
|
this.elementIds = [];
|
||||||
// Clear relations.
|
// Clear relations.
|
||||||
this.clearRelations();
|
this.clearRelations();
|
||||||
|
// Remove the click event listener.
|
||||||
|
this.containerRef.removeEventListener("click", this.handleContainerClick);
|
||||||
// Clean container.
|
// Clean container.
|
||||||
this.containerRef.innerHTML = "";
|
this.containerRef.innerHTML = "";
|
||||||
}
|
}
|
||||||
@ -526,7 +523,7 @@ export default class VisualConsole {
|
|||||||
/**
|
/**
|
||||||
* Create line elements which connect the elements with their parents.
|
* Create line elements which connect the elements with their parents.
|
||||||
*/
|
*/
|
||||||
private buildRelations(): void {
|
public buildRelations(): void {
|
||||||
// Clear relations.
|
// Clear relations.
|
||||||
this.clearRelations();
|
this.clearRelations();
|
||||||
// Add relations.
|
// Add relations.
|
||||||
@ -846,4 +843,29 @@ export default class VisualConsole {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
BIN
visual_console_client/src/lib/help-tip.png
Normal file
BIN
visual_console_client/src/lib/help-tip.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
@ -10,6 +10,8 @@ import {
|
|||||||
ItemMeta
|
ItemMeta
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
|
import helpTipIcon from "./help-tip.png";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a number or a default value from a raw value.
|
* Return a number or a default value from a raw value.
|
||||||
* @param value Raw value from which we will try to extract a valid number.
|
* @param value Raw value from which we will try to extract a valid number.
|
||||||
@ -463,13 +465,9 @@ export function addMovementListener(
|
|||||||
let borderFix = Number.parseInt(borderWidth) * 2;
|
let borderFix = Number.parseInt(borderWidth) * 2;
|
||||||
|
|
||||||
// Will run onMoved 32ms after its last execution.
|
// Will run onMoved 32ms after its last execution.
|
||||||
const debouncedMovement = debounce(32, (x: Position["x"], y: Position["y"]) =>
|
const debouncedMovement = debounce(32, onMoved);
|
||||||
onMoved(x, y)
|
|
||||||
);
|
|
||||||
// Will run onMoved one time max every 16ms.
|
// Will run onMoved one time max every 16ms.
|
||||||
const throttledMovement = throttle(16, (x: Position["x"], y: Position["y"]) =>
|
const throttledMovement = throttle(16, onMoved);
|
||||||
onMoved(x, y)
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMove = (e: MouseEvent) => {
|
const handleMove = (e: MouseEvent) => {
|
||||||
// Calculate the new element coordinates.
|
// Calculate the new element coordinates.
|
||||||
@ -641,15 +639,9 @@ export function addResizementListener(
|
|||||||
let borderFix = Number.parseInt(borderWidth);
|
let borderFix = Number.parseInt(borderWidth);
|
||||||
|
|
||||||
// Will run onResized 32ms after its last execution.
|
// Will run onResized 32ms after its last execution.
|
||||||
const debouncedResizement = debounce(
|
const debouncedResizement = debounce(32, onResized);
|
||||||
32,
|
|
||||||
(width: Size["width"], height: Size["height"]) => onResized(width, height)
|
|
||||||
);
|
|
||||||
// Will run onResized one time max every 16ms.
|
// Will run onResized one time max every 16ms.
|
||||||
const throttledResizement = throttle(
|
const throttledResizement = throttle(16, onResized);
|
||||||
16,
|
|
||||||
(width: Size["width"], height: Size["height"]) => onResized(width, height)
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleResize = (e: MouseEvent) => {
|
const handleResize = (e: MouseEvent) => {
|
||||||
// Calculate the new element coordinates.
|
// Calculate the new element coordinates.
|
||||||
@ -754,6 +746,22 @@ export function addResizementListener(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Document and code
|
||||||
export function t(text: string): string {
|
export function t(text: string): string {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function helpTip(text: string): HTMLElement {
|
||||||
|
const container = document.createElement("a");
|
||||||
|
container.className = "tip";
|
||||||
|
const icon = document.createElement("img");
|
||||||
|
icon.src = helpTipIcon;
|
||||||
|
icon.className = "forced_title";
|
||||||
|
icon.setAttribute("alt", text);
|
||||||
|
icon.setAttribute("data-title", text);
|
||||||
|
icon.setAttribute("data-use_title_for_force_title", "1");
|
||||||
|
|
||||||
|
container.appendChild(icon);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
@ -132,7 +132,10 @@ describe("itemMetaDecoder function", () => {
|
|||||||
isFromCache: false,
|
isFromCache: false,
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
editMode: false
|
editMode: false,
|
||||||
|
isBeingMoved: false,
|
||||||
|
isBeingResized: false,
|
||||||
|
isSelected: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -149,7 +152,10 @@ describe("itemMetaDecoder function", () => {
|
|||||||
isFromCache: false,
|
isFromCache: false,
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
editMode: true
|
editMode: true,
|
||||||
|
isBeingMoved: false,
|
||||||
|
isBeingResized: false,
|
||||||
|
isSelected: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user