forked from github-starred/komodo
realtime current stats heatbar
This commit is contained in:
66
frontend/src/components/shared/HeatBar.tsx
Normal file
66
frontend/src/components/shared/HeatBar.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
Component,
|
||||
createSignal,
|
||||
For,
|
||||
JSX,
|
||||
onCleanup,
|
||||
onMount,
|
||||
} from "solid-js";
|
||||
import Flex from "./layout/Flex";
|
||||
|
||||
const BAR_GAP = 4;
|
||||
const BLUE: [number, number, number] = [24, 78, 159];
|
||||
const RED: [number, number, number] = [149, 46, 35];
|
||||
|
||||
const HeatBar: Component<{
|
||||
total: number;
|
||||
filled: number;
|
||||
containerClass?: string;
|
||||
containerStyle?: JSX.CSSProperties;
|
||||
barHeight?: string;
|
||||
}> = (p) => {
|
||||
let el: HTMLDivElement;
|
||||
const [width, setWidth] = createSignal<number>();
|
||||
const handleResize = () => {
|
||||
if (el) {
|
||||
setWidth((el.clientWidth - (p.total - 1) * BAR_GAP) / p.total);
|
||||
}
|
||||
};
|
||||
onMount(() => handleResize());
|
||||
addEventListener("resize", handleResize);
|
||||
onCleanup(() => {
|
||||
removeEventListener("resize", handleResize);
|
||||
});
|
||||
return (
|
||||
<Flex ref={el!} gap={`${BAR_GAP}px`} class={p.containerClass} style={p.containerStyle}>
|
||||
<For each={[...Array(p.total).keys()]}>
|
||||
{(index) => (
|
||||
<div
|
||||
style={{
|
||||
height: p.barHeight || "2rem",
|
||||
width: `${width()!}px`,
|
||||
"background-color": index <= p.filled ? blendColors(
|
||||
BLUE,
|
||||
RED,
|
||||
index / p.total
|
||||
) : "transparent",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeatBar;
|
||||
|
||||
function blendColors(
|
||||
[r1, g1, b1]: [number, number, number],
|
||||
[r2, b2, g2]: [number, number, number],
|
||||
perc: number /* 0-1 */
|
||||
) {
|
||||
const r = Math.floor(r1 + (r2 - r1) * perc);
|
||||
const g = Math.floor(g1 + (g2 - g1) * perc);
|
||||
const b = Math.floor(b1 + (b2 - b1) * perc);
|
||||
return `rgb(${r},${g},${b})`;
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
import { Params, useParams } from "@solidjs/router";
|
||||
import ReconnectingWebSocket from "reconnecting-websocket";
|
||||
import { Component, createEffect, createSignal, Setter } from "solid-js";
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
createSignal,
|
||||
onCleanup,
|
||||
Setter,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { client, URL } from "../..";
|
||||
import { SystemStats } from "../../types";
|
||||
import { generateQuery } from "../../util/helpers";
|
||||
import { combineClasses, generateQuery } from "../../util/helpers";
|
||||
import HeatBar from "../shared/HeatBar";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import Loading from "../shared/loading/Loading";
|
||||
import s from "./stats.module.scss";
|
||||
|
||||
const CurrentStats: Component<{}> = (p) => {
|
||||
@@ -21,12 +30,54 @@ const CurrentStats: Component<{}> = (p) => {
|
||||
})
|
||||
.then(setStats);
|
||||
});
|
||||
const mem_perc = () => {
|
||||
return (100 * stats()!.mem_used_gb) / stats()!.mem_total_gb;
|
||||
};
|
||||
const disk_perc = () => {
|
||||
return (100 * stats()!.disk.used_gb) / stats()!.disk.total_gb;
|
||||
};
|
||||
return (
|
||||
<Grid class={s.Content}>
|
||||
<Flex>
|
||||
<div>cpu:</div>
|
||||
<h2>{}</h2>
|
||||
</Flex>
|
||||
<Grid class={s.Content} placeItems="start center">
|
||||
<Show when={stats()} fallback={<Loading type="three-dot" />}>
|
||||
<Grid class={s.HeatBars} placeItems="center start">
|
||||
<h1>cpu:</h1>
|
||||
<HeatBar
|
||||
containerClass="card shadow"
|
||||
containerStyle={{ width: "60vw", "min-width": "300px" }}
|
||||
filled={Math.floor(stats()!.cpu_perc)}
|
||||
total={100}
|
||||
/>
|
||||
<h1>{stats()!.cpu_perc.toFixed(1)}%</h1>
|
||||
<h1>mem:</h1>
|
||||
<HeatBar
|
||||
containerClass="card shadow"
|
||||
containerStyle={{ width: "60vw", "min-width": "300px" }}
|
||||
filled={Math.floor(mem_perc())}
|
||||
total={100}
|
||||
/>
|
||||
<Grid gap="0">
|
||||
<h1>{mem_perc().toFixed(1)}%</h1>
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
{stats()!.mem_used_gb.toFixed()}GB of{" "}
|
||||
{stats()!.mem_total_gb.toFixed()}GB
|
||||
</div>
|
||||
</Grid>
|
||||
<h1>disk:</h1>
|
||||
<HeatBar
|
||||
containerClass="card shadow"
|
||||
containerStyle={{ width: "60vw", "min-width": "300px" }}
|
||||
filled={Math.floor(disk_perc())}
|
||||
total={100}
|
||||
/>
|
||||
<Grid gap="0">
|
||||
<h1>{disk_perc().toFixed(1)}%</h1>
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
{stats()!.disk.used_gb.toFixed()}GB of{" "}
|
||||
{stats()!.disk.total_gb.toFixed()}GB
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Show>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -53,7 +104,7 @@ function useStatsWs(params: Params, setStats: Setter<SystemStats>) {
|
||||
return;
|
||||
}
|
||||
const stats = JSON.parse(data) as SystemStats;
|
||||
console.log(stats);
|
||||
// console.log(stats);
|
||||
setStats(stats);
|
||||
});
|
||||
ws.addEventListener("close", () => {
|
||||
@@ -61,6 +112,10 @@ function useStatsWs(params: Params, setStats: Setter<SystemStats>) {
|
||||
// clearInterval(int);
|
||||
setOpen(false);
|
||||
});
|
||||
onCleanup(() => {
|
||||
console.log("closing stats ws");
|
||||
ws.close();
|
||||
});
|
||||
return {
|
||||
open,
|
||||
};
|
||||
|
||||
@@ -12,4 +12,8 @@
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.HeatBars {
|
||||
grid-template-columns: repeat(3, auto);
|
||||
}
|
||||
Reference in New Issue
Block a user