Visual Console Client: fixed some lint errors and added the digital clock item

Former-commit-id: 643bc4997ec971e8892b4dcd439a2828dc4e441b
This commit is contained in:
Alejandro Gallardo Escobar 2019-02-26 17:05:30 +01:00
parent 527a27940c
commit 7a45d9f62e
17 changed files with 419 additions and 57 deletions

View File

@ -1,7 +1,8 @@
{
"env": {
"browser": true,
"node": true
"node": true,
"jest": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "prettier"],
@ -11,6 +12,7 @@
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/indent": "off"
"@typescript-eslint/indent": "off",
"no-console": "off"
}
}

View File

@ -1,5 +1,5 @@
export interface Listener<T> {
(event: T): any;
(event: T): void;
}
export interface Disposable {
@ -11,23 +11,23 @@ export default class TypedEvent<T> {
private listeners: Listener<T>[] = [];
private listenersOncer: Listener<T>[] = [];
on = (listener: Listener<T>): Disposable => {
public on = (listener: Listener<T>): Disposable => {
this.listeners.push(listener);
return {
dispose: () => this.off(listener)
};
};
once = (listener: Listener<T>): void => {
public once = (listener: Listener<T>): void => {
this.listenersOncer.push(listener);
};
off = (listener: Listener<T>): void => {
public off = (listener: Listener<T>): void => {
const callbackIndex = this.listeners.indexOf(listener);
if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
};
emit = (event: T): void => {
public emit = (event: T): void => {
/** Update any general listeners */
this.listeners.forEach(listener => listener(event));
@ -36,7 +36,5 @@ export default class TypedEvent<T> {
this.listenersOncer = [];
};
pipe = (te: TypedEvent<T>): Disposable => {
return this.on(e => te.emit(e));
};
public pipe = (te: TypedEvent<T>): Disposable => this.on(e => te.emit(e));
}

View File

@ -8,6 +8,7 @@ import StaticGraph, { staticGraphPropsDecoder } from "./items/StaticGraph";
import Icon, { iconPropsDecoder } from "./items/Icon";
import ColorCloud, { colorCloudPropsDecoder } from "./items/ColorCloud";
import Group, { groupPropsDecoder } from "./items/Group";
import Clock, { clockPropsDecoder } from "./items/Clock";
// Base properties.
export interface VisualConsoleProps extends Size {
@ -69,11 +70,12 @@ export function visualConsolePropsDecoder(
}
// TODO: Document.
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function itemInstanceFrom(data: UnknownObject) {
const type = parseIntOr(data.type, null);
if (type == null) throw new TypeError("missing item type.");
switch (<VisualConsoleItemType>type) {
switch (type as VisualConsoleItemType) {
case VisualConsoleItemType.STATIC_GRAPH:
return new StaticGraph(staticGraphPropsDecoder(data));
case VisualConsoleItemType.MODULE_GRAPH:
@ -113,7 +115,7 @@ function itemInstanceFrom(data: UnknownObject) {
case VisualConsoleItemType.BARS_GRAPH:
throw new TypeError("item not found");
case VisualConsoleItemType.CLOCK:
throw new TypeError("item not found");
return new Clock(clockPropsDecoder(data));
case VisualConsoleItemType.COLOR_CLOUD:
return new ColorCloud(colorCloudPropsDecoder(data));
default:
@ -129,7 +131,7 @@ export default class VisualConsole {
// Visual Console Item instances.
private elements: VisualConsoleItem<VisualConsoleItemProps>[] = [];
constructor(
public constructor(
container: HTMLElement,
props: VisualConsoleProps,
items: UnknownObject[]
@ -167,7 +169,7 @@ export default class VisualConsole {
* Public accessor of the `props` property.
* @return Properties.
*/
get props(): VisualConsoleProps {
public get props(): VisualConsoleProps {
return this._props;
}
@ -177,7 +179,7 @@ export default class VisualConsole {
* stored props, a render would be fired.
* @param newProps
*/
set props(newProps: VisualConsoleProps) {
public set props(newProps: VisualConsoleProps) {
const prevProps = this.props;
// Update the internal props.
this._props = newProps;
@ -192,7 +194,7 @@ export default class VisualConsole {
* 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.
*/
render(prevProps: VisualConsoleProps | null = null): void {
public render(prevProps: VisualConsoleProps | null = null): void {
if (prevProps) {
if (prevProps.backgroundURL !== this.props.backgroundURL) {
this.containerRef.style.backgroundImage = this.props.backgroundURL;
@ -217,7 +219,7 @@ export default class VisualConsole {
* @param newSize
* @return Whether the size changed or not.
*/
sizeChanged(prevSize: Size, newSize: Size): boolean {
public sizeChanged(prevSize: Size, newSize: Size): boolean {
return (
prevSize.width !== newSize.width || prevSize.height !== newSize.height
);
@ -228,7 +230,7 @@ export default class VisualConsole {
* @param width
* @param height
*/
resizeElement(width: number, height: number): void {
public resizeElement(width: number, height: number): void {
this.containerRef.style.width = `${width}px`;
this.containerRef.style.height = `${height}px`;
}
@ -238,7 +240,7 @@ export default class VisualConsole {
* @param width
* @param height
*/
resize(width: number, height: number): void {
public resize(width: number, height: number): void {
this.props = {
...this.props, // Object spread: http://es6-features.org/#SpreadOperator
width,
@ -249,7 +251,7 @@ export default class VisualConsole {
/**
* To remove the event listeners and the elements from the DOM.
*/
remove(): void {
public remove(): void {
this.elements.forEach(e => e.remove()); // Arrow function.
this.elements = [];
}

View File

@ -45,16 +45,18 @@ export interface VisualConsoleItemProps extends Position, Size {
}
// FIXME: Fix type compatibility.
export type ItemClickEvent<ItemProps extends VisualConsoleItemProps> = {
export interface ItemClickEvent<ItemProps extends VisualConsoleItemProps> {
// data: ItemProps;
data: UnknownObject;
};
}
/**
* Extract a valid enum value from a raw label position value.
* @param labelPosition Raw value.
*/
const parseLabelPosition = (labelPosition: any) => {
const parseLabelPosition = (
labelPosition: any // eslint-disable-line @typescript-eslint/no-explicit-any
): VisualConsoleItemProps["labelPosition"] => {
switch (labelPosition) {
case "up":
case "right":
@ -123,7 +125,7 @@ abstract class VisualConsoleItem<ItemProps extends VisualConsoleItemProps> {
*/
abstract createDomElement(): HTMLElement;
constructor(props: ItemProps) {
public constructor(props: ItemProps) {
this.itemProps = props;
/*
@ -166,7 +168,7 @@ abstract class VisualConsoleItem<ItemProps extends VisualConsoleItemProps> {
* Public accessor of the `props` property.
* @return Properties.
*/
get props(): ItemProps {
public get props(): ItemProps {
return this.itemProps;
}
@ -176,7 +178,7 @@ abstract class VisualConsoleItem<ItemProps extends VisualConsoleItemProps> {
* stored props, a render would be fired.
* @param newProps
*/
set props(newProps: ItemProps) {
public set props(newProps: ItemProps) {
const prevProps = this.props;
// Update the internal props.
this.itemProps = newProps;
@ -206,7 +208,7 @@ abstract class VisualConsoleItem<ItemProps extends VisualConsoleItemProps> {
* To recreate or update the HTMLElement which represents the item into the DOM.
* @param prevProps If exists it will be used to only perform DOM updates instead of a full replace.
*/
render(prevProps: ItemProps | null = null): void {
public render(prevProps: ItemProps | null = null): void {
// Move box.
if (!prevProps || prevProps.x !== this.props.x) {
this.elementRef.style.left = `${this.props.x}px`;
@ -228,7 +230,7 @@ abstract class VisualConsoleItem<ItemProps extends VisualConsoleItemProps> {
/**
* To remove the event listeners and the elements from the DOM.
*/
remove(): void {
public remove(): void {
// Event listeners.
this.disposables.forEach(_ => _.dispose());
// VisualConsoleItem extension DOM element.
@ -242,7 +244,7 @@ abstract class VisualConsoleItem<ItemProps extends VisualConsoleItemProps> {
* @param x Horizontal axis position.
* @param y Vertical axis position.
*/
move(x: number, y: number): void {
public move(x: number, y: number): void {
// Compare position.
if (x === this.props.x && y === this.props.y) return;
// Update position. Change itemProps instead of props to avoid re-render.
@ -258,7 +260,7 @@ abstract class VisualConsoleItem<ItemProps extends VisualConsoleItemProps> {
* @param width Width.
* @param height Height.
*/
resize(width: number, height: number): void {
public resize(width: number, height: number): void {
// Compare size.
if (width === this.props.width && height === this.props.height) return;
// Update size. Change itemProps instead of props to avoid re-render.
@ -273,7 +275,7 @@ abstract class VisualConsoleItem<ItemProps extends VisualConsoleItemProps> {
* 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.
*/
onClick(listener: Listener<ItemClickEvent<ItemProps>>): void {
public onClick(listener: Listener<ItemClickEvent<ItemProps>>): void {
/*
* The '.on' function returns a function which will clean the event
* listener when executed. We store all the 'dispose' functions to

View File

@ -5,10 +5,7 @@
* https://www.typescriptlang.org/
*/
import VisualConsole, {
visualConsolePropsDecoder,
VisualConsoleProps
} from "./VisualConsole";
import VisualConsole, { visualConsolePropsDecoder } from "./VisualConsole";
// declare global {
// interface Window {
@ -28,7 +25,7 @@ if (container != null) {
width: 800,
height: 300,
backgroundURL: null,
backgroundColor: "#000000",
backgroundColor: "rgb(154, 154, 154)",
isFavorite: false
};
@ -62,7 +59,7 @@ if (container != null) {
const colorCloudRawProps = {
// Generic props.
id: 2,
type: 20, // Static graph = 0
type: 20, // Color cloud = 20
label: null,
labelText: "CLOUD",
isLinkEnabled: false,
@ -85,11 +82,34 @@ if (container != null) {
color: "rgb(100, 50, 245)"
};
const digitalClockRawProps = {
// Generic props.
id: 2,
type: 19, // clock = 19
label: null,
isLinkEnabled: false,
isOnTop: false,
parentId: null,
aclGroupId: null,
// Position props.
x: 500,
y: 100,
// Size props.
width: 300,
height: 150,
// Custom props.
clockType: "digital",
clockFormat: "datetime",
clockTimezone: "Madrid",
clockTimezoneOffset: 60,
showClockTimezone: true
};
try {
const visualConsole = new VisualConsole(
container,
visualConsolePropsDecoder(rawProps),
[staticGraphRawProps, colorCloudRawProps]
[staticGraphRawProps, colorCloudRawProps, digitalClockRawProps]
);
console.log(visualConsole);
} catch (error) {

View File

@ -0,0 +1,73 @@
import Clock, { clockPropsDecoder } from "./Clock";
const genericRawProps = {
id: 1,
type: 19, // Clock item = 19
label: null,
isLinkEnabled: false,
isOnTop: false,
parentId: null,
aclGroupId: null
};
const positionRawProps = {
x: 100,
y: 50
};
const sizeRawProps = {
width: 100,
height: 100
};
const digitalClockProps = {
clockType: "digital",
clockFormat: "datetime",
clockTimezone: "Madrid",
clockTimezoneOffset: 60
};
const linkedModuleProps = {
// Agent props.
agentId: null,
agentName: null,
// Module props.
moduleId: null,
moduleName: null
};
describe("Clock item", () => {
const clockInstance = new Clock(
clockPropsDecoder({
...genericRawProps,
...positionRawProps,
...sizeRawProps,
...linkedModuleProps,
...digitalClockProps
})
);
it("should have the digital-clock class", () => {
expect(
clockInstance.elementRef.getElementsByClassName("digital-clock").length
).toBeGreaterThan(0);
});
describe("getDate function", () => {
it("should return the date with padded 0's", () => {
const expected = "01/02/0123";
const date = new Date(`02/01/0123 12:00:00`);
const digitalDate = clockInstance.getDigitalDate(date);
expect(digitalDate).toBe(expected);
});
});
describe("getTime function", () => {
it("should return the time with padded 0's when hours/minutes/seconds are less than 10", () => {
const expected = "01:02:03";
const date = new Date(`01/01/1970 ${expected}`);
const digitalTime = clockInstance.getDigitalTime(date);
expect(digitalTime).toBe(expected);
});
});
});

View File

@ -0,0 +1,180 @@
import { LinkedVisualConsoleProps, UnknownObject } from "../types";
import {
linkedVCPropsDecoder,
parseIntOr,
padLeft,
parseBoolean
} from "../lib";
import VisualConsoleItem, {
VisualConsoleItemProps,
itemBasePropsDecoder,
VisualConsoleItemType
} from "../VisualConsoleItem";
export type ClockProps = {
type: VisualConsoleItemType.CLOCK;
clockType: "analogic" | "digital";
clockFormat: "datetime" | "time";
clockTimezone: string;
clockTimezoneOffset: number; // Offset of the timezone to UTC in seconds.
showClockTimezone: boolean;
} & VisualConsoleItemProps &
LinkedVisualConsoleProps;
/**
* Extract a valid enum value from a raw unknown value.
* @param clockType Raw value.
*/
const parseClockType = (
clockType: any // eslint-disable-line @typescript-eslint/no-explicit-any
): ClockProps["clockType"] => {
switch (clockType) {
case "analogic":
case "digital":
return clockType;
default:
return "analogic";
}
};
/**
* Extract a valid enum value from a raw unknown value.
* @param clockFormat Raw value.
*/
const parseClockFormat = (
clockFormat: any // eslint-disable-line @typescript-eslint/no-explicit-any
): ClockProps["clockFormat"] => {
switch (clockFormat) {
case "datetime":
case "date":
case "time":
return clockFormat;
default:
return "datetime";
}
};
/**
* 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 clock props.
* @throws Will throw a TypeError if some property
* is missing from the raw object or have an invalid type.
*/
export function clockPropsDecoder(data: UnknownObject): ClockProps | never {
if (
typeof data.clockTimezone !== "string" ||
data.clockTimezone.length === 0
) {
throw new TypeError("invalid timezone.");
}
return {
...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.
type: VisualConsoleItemType.CLOCK,
clockType: parseClockType(data.clockType),
clockFormat: parseClockFormat(data.clockFormat),
clockTimezone: data.clockTimezone,
clockTimezoneOffset: parseIntOr(data.clockTimezoneOffset, 0),
showClockTimezone: parseBoolean(data.showClockTimezone),
...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.
};
}
export default class Clock extends VisualConsoleItem<ClockProps> {
public createDomElement(): HTMLElement {
switch (this.props.clockType) {
case "analogic":
throw new Error("not implemented.");
case "digital":
return this.createDigitalClock();
}
}
public createDigitalClock(): HTMLElement {
const element: HTMLDivElement = document.createElement("div");
element.className = "digital-clock";
// The proportion of the clock should be (height * 2 = width) aproximately.
const width =
this.props.height * 2 < this.props.width
? this.props.height * 2
: this.props.width;
this.props.clockTimezone = "Madrid";
const baseTimeFontSize = 20; // Per 100px of width.
const dateFontSizeMultiplier = 0.5;
const tzFontSizeMultiplier = 6 / this.props.clockTimezone.length;
const timeFontSize = (baseTimeFontSize * width) / 100;
const dateFontSize =
(baseTimeFontSize * dateFontSizeMultiplier * width) / 100;
const tzFontSize = Math.min(
(baseTimeFontSize * tzFontSizeMultiplier * width) / 100,
(width / 100) * 10
);
// Date.
if (this.props.clockFormat === "datetime") {
const dateElem: HTMLSpanElement = document.createElement("span");
dateElem.className = "date";
dateElem.textContent = this.getDigitalDate();
dateElem.style.fontSize = `${dateFontSize}px`;
element.append(dateElem);
}
// Time.
const timeElem: HTMLSpanElement = document.createElement("span");
timeElem.className = "time";
timeElem.textContent = this.getDigitalTime();
timeElem.style.fontSize = `${timeFontSize}px`;
element.append(timeElem);
// Timezone name.
if (this.props.showClockTimezone) {
const tzElem: HTMLSpanElement = document.createElement("span");
tzElem.className = "timezone";
tzElem.textContent = this.props.clockTimezone;
tzElem.style.fontSize = `${tzFontSize}px`;
element.append(tzElem);
}
return element;
}
/**
* Generate the current date using the timezone offset stored into the properties.
* @return The current date.
*/
public getDate(): Date {
const d = new Date();
const targetTZOffset = this.props.clockTimezoneOffset * 60 * 1000; // In ms.
const localTZOffset = d.getTimezoneOffset() * 60 * 1000; // In ms.
const utimestamp = d.getTime() + targetTZOffset + localTZOffset;
return new Date(utimestamp);
}
public getDigitalDate(initialDate: Date | null = null): string {
const date = initialDate || this.getDate();
// Use getDate, getDay returns the week day.
const day = padLeft(date.getDate(), 2, 0);
// The getMonth function returns the month starting by 0.
const month = padLeft(date.getMonth() + 1, 2, 0);
const year = padLeft(date.getFullYear(), 4, 0);
// Format: 'd/m/Y'.
return `${day}/${month}/${year}`;
}
public getDigitalTime(initialDate: Date | null = null): string {
const date = initialDate || this.getDate();
const hours = padLeft(date.getHours(), 2, 0);
const minutes = padLeft(date.getMinutes(), 2, 0);
const seconds = padLeft(date.getSeconds(), 2, 0);
return `${hours}:${minutes}:${seconds}`;
}
}

View File

@ -34,7 +34,7 @@ const linkedModuleProps = {
};
describe("Color cloud item", () => {
const groupInstance = new ColorCloud(
const colorCloudInstance = new ColorCloud(
colorCloudPropsDecoder({
...genericRawProps,
...positionRawProps,
@ -46,7 +46,7 @@ describe("Color cloud item", () => {
it("should have the color-cloud class", () => {
expect(
groupInstance.elementRef.getElementsByClassName("color-cloud").length
colorCloudInstance.elementRef.getElementsByClassName("color-cloud").length
).toBeGreaterThan(0);
});
});

View File

@ -49,7 +49,7 @@ export function colorCloudPropsDecoder(
const svgNS = "http://www.w3.org/2000/svg";
export default class ColorCloud extends VisualConsoleItem<ColorCloudProps> {
createDomElement(): HTMLElement {
public createDomElement(): HTMLElement {
const container: HTMLDivElement = document.createElement("div");
container.className = "color-cloud";
@ -59,7 +59,7 @@ export default class ColorCloud extends VisualConsoleItem<ColorCloudProps> {
return container;
}
createSvgElement(): SVGSVGElement {
public createSvgElement(): SVGSVGElement {
const gradientId = `grad_${this.props.id}`;
// SVG container.
const svg = document.createElementNS(svgNS, "svg");
@ -108,12 +108,11 @@ export default class ColorCloud extends VisualConsoleItem<ColorCloudProps> {
/**
* @override VisualConsoleItem.resize
* To resize the item.
* @param width Width.
* @param height Height.
* @param diameter Diameter.
*/
resize(width: number, height: number): void {
// Resize parent. Use only the width, cause this element only needs a diameter.
super.resize(width, width);
public resize(diameter: number): void {
// Resize parent. Use the diameter as width and height.
super.resize(diameter, diameter);
// Get SVG element.
const svgElement = this.elementRef.getElementsByTagName("svg").item(0);

View File

@ -42,7 +42,7 @@ export function groupPropsDecoder(data: UnknownObject): GroupProps | never {
}
export default class Group extends VisualConsoleItem<GroupProps> {
createDomElement(): HTMLElement {
public createDomElement(): HTMLElement {
const img: HTMLImageElement = document.createElement("img");
img.className = "group";
img.src = this.props.imageSrc;

View File

@ -37,7 +37,7 @@ export function iconPropsDecoder(data: UnknownObject): IconProps | never {
}
export default class Icon extends VisualConsoleItem<IconProps> {
createDomElement(): HTMLElement {
public createDomElement(): HTMLElement {
const img: HTMLImageElement = document.createElement("img");
img.className = "icon";
img.src = this.props.imageSrc;

View File

@ -23,7 +23,9 @@ export type StaticGraphProps = {
* Extract a valid enum value from a raw unknown value.
* @param showLastValueTooltip Raw value.
*/
const parseShowLastValueTooltip = (showLastValueTooltip: any) => {
const parseShowLastValueTooltip = (
showLastValueTooltip: any // eslint-disable-line @typescript-eslint/no-explicit-any
): StaticGraphProps["showLastValueTooltip"] => {
switch (showLastValueTooltip) {
case "default":
case "enabled":
@ -61,7 +63,7 @@ export function staticGraphPropsDecoder(
}
export default class StaticGraph extends VisualConsoleItem<StaticGraphProps> {
createDomElement(): HTMLElement {
public createDomElement(): HTMLElement {
const img: HTMLImageElement = document.createElement("img");
img.className = "static-graph";
img.src = this.props.imageSrc;

View File

@ -1,4 +1,4 @@
import { parseIntOr } from "./lib";
import { parseIntOr, padLeft } from "./lib";
describe("function parseIntOr", () => {
it("should retrieve valid int or a default value", () => {
@ -11,3 +11,18 @@ describe("function parseIntOr", () => {
expect(parseIntOr(1, null)).toBe(1);
});
});
describe("function padLeft", () => {
it("should pad properly", () => {
expect(padLeft(1, 2, 0)).toBe("01");
expect(padLeft(1, 4, 0)).toBe("0001");
expect(padLeft(1, 4, "0")).toBe("0001");
expect(padLeft("1", 4, "0")).toBe("0001");
expect(padLeft(10, 4, 0)).toBe("0010");
expect(padLeft("bar", 6, "foo")).toBe("foobar");
expect(padLeft("bar", 11, "foo")).toBe("foofoofobar");
expect(padLeft("bar", 4, "foo")).toBe("fbar");
expect(padLeft("bar", 2, "foo")).toBe("ar");
expect(padLeft("bar", 3, "foo")).toBe("bar");
});
});

View File

@ -14,6 +14,7 @@ import {
* @param defaultValue Default value to use if we cannot extract a valid number.
* @return A valid number or the default value.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function parseIntOr<T>(value: any, defaultValue: T): number | T {
if (typeof value === "number") return value;
if (typeof value === "string" && value.length > 0 && !isNaN(parseInt(value)))
@ -26,6 +27,7 @@ export function parseIntOr<T>(value: any, defaultValue: T): number | T {
* @param value Raw value from which we will try to extract the boolean.
* @return A valid boolean value. false by default.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function parseBoolean(value: any): boolean {
if (typeof value === "boolean") return value;
else if (typeof value === "number") return value > 0;
@ -33,6 +35,42 @@ export function parseBoolean(value: any): boolean {
else return false;
}
/**
* Pad the current string with another string (multiple times, if needed)
* until the resulting string reaches the given length.
* The padding is applied from the start (left) of the current string.
* @param value Text that needs to be padded.
* @param length Length of the returned text.
* @param pad Text to add.
* @return Padded text.
*/
export function padLeft(
value: string | number,
length: number,
pad: string | number = " "
): string {
if (typeof value === "number") value = `${value}`;
if (typeof pad === "number") pad = `${pad}`;
const diffLength = length - value.length;
if (diffLength === 0) return value;
if (diffLength < 0) return value.substr(Math.abs(diffLength));
if (diffLength === pad.length) return `${pad}${value}`;
if (diffLength < pad.length) return `${pad.substring(0, diffLength)}${value}`;
const repeatTimes = Math.floor(diffLength / pad.length);
const restLength = diffLength - pad.length * repeatTimes;
let newPad = "";
for (let i = 0; i < repeatTimes; i++) newPad += pad;
if (restLength === 0) return `${newPad}${value}`;
return `${newPad}${pad.substring(0, restLength)}${value}`;
}
/* Decoders */
/**
* Build a valid typed object from a raw object.
* @param data Raw object.
@ -125,7 +163,7 @@ export function linkedVCPropsDecoder(
linkedLayoutStatusType: "default"
};
switch (data.linkedLayoutStatusType) {
case "weight":
case "weight": {
const weight = parseIntOr(data.linkedLayoutStatusTypeWeight, null);
if (weight == null)
throw new TypeError("invalid status calculation properties.");
@ -136,7 +174,8 @@ export function linkedVCPropsDecoder(
linkedLayoutStatusTypeWeight: weight
};
break;
case "service":
}
case "service": {
const warningThreshold = parseIntOr(
data.linkedLayoutStatusTypeWarningThreshold,
null
@ -155,6 +194,7 @@ export function linkedVCPropsDecoder(
linkedLayoutStatusTypeCriticalThreshold: criticalThreshold
};
break;
}
}
const linkedLayoutBaseProps = {

View File

@ -1,6 +1,6 @@
export type UnknownObject = {
[key: string]: any;
};
export interface UnknownObject {
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export interface Position {
x: number;

Binary file not shown.

View File

@ -6,3 +6,32 @@
width: inherit;
height: inherit;
}
/* Clock item */
@font-face {
font-family: Alarm Clock;
src: url(alarm-clock.ttf);
}
.visual-console-item .digital-clock {
display: flex;
flex-direction: column;
justify-content: center;
justify-items: center;
align-content: center;
align-items: center;
}
.visual-console-item .digital-clock > span {
font-family: "Alarm Clock", "Courier New", Courier, monospace;
font-size: 50px;
}
.visual-console-item .digital-clock > span.date {
font-size: 25px;
}
.visual-console-item .digital-clock > span.timezone {
font-size: 25px;
}