diff --git a/visual_console_client/src/Form.ts b/visual_console_client/src/Form.ts index a7807cfd32..08ba3dcd68 100644 --- a/visual_console_client/src/Form.ts +++ b/visual_console_client/src/Form.ts @@ -147,7 +147,7 @@ export class FormContainer { } public removeInputGroup(inputGroupName: string): FormContainer { - // delete this.inputGroupsByName[inputGroupName]; + delete this.inputGroupsByName[inputGroupName]; // Remove the current stored name. this.enabledInputGroupNames = this.enabledInputGroupNames.filter( name => name === inputGroupName diff --git a/visual_console_client/src/Item.ts b/visual_console_client/src/Item.ts index ba10ff4b5f..ad54fdec87 100644 --- a/visual_console_client/src/Item.ts +++ b/visual_console_client/src/Item.ts @@ -17,7 +17,8 @@ import { addMovementListener, debounce, addResizementListener, - t + t, + helpTip } from "./lib"; import TypedEvent, { Listener, Disposable } from "./lib/TypedEvent"; import { FormContainer, InputGroup } from "./Form"; @@ -84,7 +85,76 @@ export interface ItemResizedEvent { } // TODO: Document -class PositionInputGroup extends InputGroup { +class LinkInputGroup extends InputGroup> { + 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> { + 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> { protected createContent(): HTMLElement | HTMLElement[] { const positionLabel = document.createElement("label"); positionLabel.textContent = t("Position"); @@ -118,8 +188,54 @@ class PositionInputGroup extends InputGroup { } } +// TODO: Document +class SizeInputGroup extends InputGroup> { + 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. */ const parseLabelPosition = ( @@ -380,7 +496,8 @@ abstract class VisualConsoleItem { this.childElementRef = this.createDomElement(); // 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. this.resizeElement(this.itemProps.width, this.itemProps.height); @@ -468,8 +585,10 @@ abstract class VisualConsoleItem { const cell = document.createElement("td"); cell.innerHTML = label; - row.append(cell); - table.append(emptyRow1, row, emptyRow2); + row.appendChild(cell); + table.appendChild(emptyRow1); + table.appendChild(row); + table.appendChild(emptyRow2); table.style.textAlign = "center"; // Change the table size depending on its position. @@ -491,7 +610,7 @@ abstract class VisualConsoleItem { } // element.innerHTML = this.props.label; - element.append(table); + element.appendChild(table); } return element; @@ -897,15 +1016,6 @@ abstract class VisualConsoleItem { }; } - // 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. * @param listener Function which is going to be executed when a linked console is clicked. @@ -985,6 +1095,25 @@ abstract class VisualConsoleItem { return disposable; } + + // TODO: Document + public getFormContainer(): FormContainer { + return VisualConsoleItem.getFormContainer(this.props); + } + + // TODO: Document + public static getFormContainer(props: Partial): 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; diff --git a/visual_console_client/src/VisualConsole.ts b/visual_console_client/src/VisualConsole.ts index f4561fffff..0c24f6d5c2 100644 --- a/visual_console_client/src/VisualConsole.ts +++ b/visual_console_client/src/VisualConsole.ts @@ -314,24 +314,7 @@ export default class VisualConsole { }); // Initialize the items. - items.forEach(item => { - 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); - } - }); + items.forEach(item => this.addElement(item, this)); // Create lines. this.buildRelations(); @@ -350,6 +333,29 @@ export default class VisualConsole { .filter(_ => _ != null) as Item[]; } + /** + * 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. * @param items. @@ -376,18 +382,7 @@ export default class VisualConsole { if (item.id) { if (this.elementsById[item.id] == null) { // New item. - try { - 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); - } + this.addElement(item); } else { // Update item. try { @@ -519,6 +514,8 @@ export default class VisualConsole { this.elementIds = []; // Clear relations. this.clearRelations(); + // Remove the click event listener. + this.containerRef.removeEventListener("click", this.handleContainerClick); // Clean container. this.containerRef.innerHTML = ""; } @@ -526,7 +523,7 @@ export default class VisualConsole { /** * Create line elements which connect the elements with their parents. */ - private buildRelations(): void { + public buildRelations(): void { // Clear relations. this.clearRelations(); // 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 + }; } diff --git a/visual_console_client/src/lib/help-tip.png b/visual_console_client/src/lib/help-tip.png new file mode 100644 index 0000000000..f2704b34ac Binary files /dev/null and b/visual_console_client/src/lib/help-tip.png differ diff --git a/visual_console_client/src/lib/index.ts b/visual_console_client/src/lib/index.ts index 7d69d948e4..8809364bbf 100644 --- a/visual_console_client/src/lib/index.ts +++ b/visual_console_client/src/lib/index.ts @@ -10,6 +10,8 @@ import { ItemMeta } from "./types"; +import helpTipIcon from "./help-tip.png"; + /** * 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. @@ -463,13 +465,9 @@ export function addMovementListener( let borderFix = Number.parseInt(borderWidth) * 2; // Will run onMoved 32ms after its last execution. - const debouncedMovement = debounce(32, (x: Position["x"], y: Position["y"]) => - onMoved(x, y) - ); + const debouncedMovement = debounce(32, onMoved); // Will run onMoved one time max every 16ms. - const throttledMovement = throttle(16, (x: Position["x"], y: Position["y"]) => - onMoved(x, y) - ); + const throttledMovement = throttle(16, onMoved); const handleMove = (e: MouseEvent) => { // Calculate the new element coordinates. @@ -641,15 +639,9 @@ export function addResizementListener( let borderFix = Number.parseInt(borderWidth); // Will run onResized 32ms after its last execution. - const debouncedResizement = debounce( - 32, - (width: Size["width"], height: Size["height"]) => onResized(width, height) - ); + const debouncedResizement = debounce(32, onResized); // Will run onResized one time max every 16ms. - const throttledResizement = throttle( - 16, - (width: Size["width"], height: Size["height"]) => onResized(width, height) - ); + const throttledResizement = throttle(16, onResized); const handleResize = (e: MouseEvent) => { // Calculate the new element coordinates. @@ -754,6 +746,22 @@ export function addResizementListener( }; } +// TODO: Document and code export function t(text: string): string { 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; +} diff --git a/visual_console_client/src/lib/spec.ts b/visual_console_client/src/lib/spec.ts index ae8ffb02cc..e4042bc018 100644 --- a/visual_console_client/src/lib/spec.ts +++ b/visual_console_client/src/lib/spec.ts @@ -132,7 +132,10 @@ describe("itemMetaDecoder function", () => { isFromCache: false, isFetching: false, isUpdating: false, - editMode: false + editMode: false, + isBeingMoved: false, + isBeingResized: false, + isSelected: false }); }); @@ -149,7 +152,10 @@ describe("itemMetaDecoder function", () => { isFromCache: false, isFetching: false, isUpdating: false, - editMode: true + editMode: true, + isBeingMoved: false, + isBeingResized: false, + isSelected: false }); });