Add switches to static card
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
background-color: var(--color-background0);
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
height: max-content;
|
||||
min-height: max-content;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -20,18 +20,17 @@
|
||||
|
||||
@media (min-width: 640px) {
|
||||
@media (min-height: 640px) {
|
||||
height: 100vh;
|
||||
|
||||
> div {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.chart {
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
@media (orientation: landscape) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
height: 100vh;
|
||||
|
||||
> div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: portrait) {
|
||||
|
||||
4
src/client/atoms/index.ts
Normal file
4
src/client/atoms/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { atom } from 'jotai';
|
||||
|
||||
export const highFpsAtom = atom(true);
|
||||
export const siAtom = atom(false);
|
||||
@@ -8,7 +8,6 @@ import { YAxis } from './y-axis';
|
||||
const stepWindow = Number(import.meta.env.CLIENT_GRAPH_STEPS);
|
||||
const stepPeriod = Number(import.meta.env.CLIENT_REFETCH_INTERVAL);
|
||||
const xMargin = 4;
|
||||
const fps = 30;
|
||||
|
||||
type Props = {
|
||||
total: number;
|
||||
@@ -33,7 +32,7 @@ export const CanvasChart = ({ total, hueOffset = 0, domain, hardDomain, data, fo
|
||||
}
|
||||
|
||||
if (!domain || historyMax > domain[1]) {
|
||||
return 1.25 * historyMax;
|
||||
return historyMax;
|
||||
}
|
||||
|
||||
return domain[1];
|
||||
@@ -129,7 +128,7 @@ export const CanvasChart = ({ total, hueOffset = 0, domain, hardDomain, data, fo
|
||||
|
||||
drawSeries('fill');
|
||||
drawSeries('stroke');
|
||||
}, fps);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='chart'>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { siAtom } from '@/atoms';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { ChartCard } from './common/card';
|
||||
|
||||
export const Disks = () => {
|
||||
const { data: dynamicData } = useQuery<DynamicData>({ queryKey: ['dynamic'] });
|
||||
const isSi = useAtomValue(siAtom);
|
||||
|
||||
if (!dynamicData) {
|
||||
return <div />;
|
||||
@@ -15,7 +18,7 @@ export const Disks = () => {
|
||||
labels: ['Read', 'Write'],
|
||||
}}
|
||||
hueOffset={120}
|
||||
formatOptions={{ units: 'B/s' }}
|
||||
formatOptions={{ units: 'B/s', ...(isSi && { si: true }) }}
|
||||
data={[dynamicData.disks.read, dynamicData.disks.write]}
|
||||
total={2}
|
||||
/>
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import { siAtom } from '@/atoms';
|
||||
import { formatValue } from '@/utils/format';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMemo } from 'react';
|
||||
import { ChartCard } from './common/card';
|
||||
|
||||
const formatOptions = { units: 'B' };
|
||||
|
||||
export const Memory = () => {
|
||||
const { data: staticData } = useQuery<StaticData>({ queryKey: ['static'] });
|
||||
const { data: dynamicData } = useQuery<DynamicData>({ queryKey: ['dynamic'] });
|
||||
const isSi = useAtomValue(siAtom);
|
||||
const formatOptions = { units: 'B', ...(isSi && { si: true }) };
|
||||
|
||||
const formatedTotals = useMemo(() => {
|
||||
if (!staticData) {
|
||||
return [];
|
||||
}
|
||||
return [formatValue(staticData.total_memory, formatOptions), formatValue(staticData.total_swap, formatOptions)];
|
||||
}, [staticData]);
|
||||
}, [staticData, formatOptions]);
|
||||
|
||||
if (!staticData || !dynamicData) {
|
||||
return <div />;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { siAtom } from '@/atoms';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { ChartCard } from './common/card';
|
||||
|
||||
export const Network = () => {
|
||||
const { data: dynamicData } = useQuery<DynamicData>({ queryKey: ['dynamic'] });
|
||||
const isSi = useAtomValue(siAtom);
|
||||
|
||||
if (!dynamicData) {
|
||||
return <div />;
|
||||
@@ -15,7 +18,7 @@ export const Network = () => {
|
||||
labels: ['Down', 'Up'],
|
||||
}}
|
||||
hueOffset={60}
|
||||
formatOptions={{ units: 'B/s' }}
|
||||
formatOptions={{ units: 'B/s', ...(isSi && { si: true }) }}
|
||||
data={[dynamicData.network.down, dynamicData.network.up]}
|
||||
total={2}
|
||||
/>
|
||||
|
||||
7
src/client/components/chart-cards/static.css
Normal file
7
src/client/components/chart-cards/static.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.switches {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 4px 0px;
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import { highFpsAtom, siAtom } from '@/atoms';
|
||||
import { Switch } from '@/components/switch';
|
||||
import { useAnimationFrame } from '@/hooks/use-animation-frame';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Switch } from '../switch';
|
||||
import './static.css';
|
||||
|
||||
const formatUptime = (value: number) => {
|
||||
const seconds = String(Math.floor(value % 60)).padStart(2, '0');
|
||||
@@ -40,6 +43,8 @@ export const Static = () => {
|
||||
const { data: staticData } = useQuery<StaticData>({ queryKey: ['static'] });
|
||||
const root = useRef(document.getElementById('root')!);
|
||||
const [dark, setDark] = useState(window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
const [highFps, setHighFps] = useAtom(highFpsAtom);
|
||||
const [isSi, setIsSi] = useAtom(siAtom);
|
||||
|
||||
useEffect(() => {
|
||||
root.current.setAttribute('data-theme', dark ? 'dark' : 'light');
|
||||
@@ -59,11 +64,25 @@ export const Static = () => {
|
||||
<h3>{staticData.kernel_version}</h3>
|
||||
{staticData.boot_time && <Uptime boot_time={staticData.boot_time} />}
|
||||
|
||||
<Switch
|
||||
checked={dark}
|
||||
label={`${dark ? 'Dark' : 'Light'} theme`}
|
||||
onChange={({ target }) => setDark(target.checked)}
|
||||
/>
|
||||
<div className='switches'>
|
||||
<Switch
|
||||
checked={dark}
|
||||
label={`${dark ? 'Dark' : 'Light'} theme`}
|
||||
onChange={({ target }) => setDark(target.checked)}
|
||||
/>
|
||||
|
||||
<Switch
|
||||
checked={highFps}
|
||||
label={`${highFps ? 'High' : 'Low'} FPS`}
|
||||
onChange={({ target }) => setHighFps(target.checked)}
|
||||
/>
|
||||
|
||||
<Switch
|
||||
checked={isSi}
|
||||
label={`Powers of ${isSi ? 10 : 2}`}
|
||||
onChange={({ target }) => setIsSi(target.checked)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { highFpsAtom } from '@/atoms';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export const useAnimationFrame = (callback: (dt: number) => void, fps = 60) => {
|
||||
export const useAnimationFrame = (callback: (dt: number) => void, fps?: number) => {
|
||||
const ignored = useRef(0);
|
||||
const requestRef = useRef<number>();
|
||||
const previousTimeRef = useRef<number>();
|
||||
const highFps = useAtomValue(highFpsAtom);
|
||||
const autoFps = fps ?? (highFps ? 30 : 4);
|
||||
|
||||
useEffect(() => {
|
||||
const animate: FrameRequestCallback = time => {
|
||||
@@ -11,7 +15,7 @@ export const useAnimationFrame = (callback: (dt: number) => void, fps = 60) => {
|
||||
const deltaTime = time - previousTimeRef.current;
|
||||
ignored.current += deltaTime;
|
||||
|
||||
if (ignored.current > 1000 / fps) {
|
||||
if (ignored.current > 1000 / autoFps) {
|
||||
ignored.current = 0;
|
||||
callback(deltaTime);
|
||||
}
|
||||
@@ -26,5 +30,5 @@ export const useAnimationFrame = (callback: (dt: number) => void, fps = 60) => {
|
||||
cancelAnimationFrame(requestRef.current);
|
||||
}
|
||||
};
|
||||
}, [callback, fps]);
|
||||
}, [callback, autoFps]);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user