feat(system-resources-widget): add has-shadow option (#4093)
This commit is contained in:
@@ -87,6 +87,7 @@
|
|||||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.5",
|
"nanoid@>=4.0.0 <5.0.9": ">=5.1.5",
|
||||||
"prismjs@<1.30.0": ">=1.30.0",
|
"prismjs@<1.30.0": ">=1.30.0",
|
||||||
"proxmox-api>undici": "7.16.0",
|
"proxmox-api>undici": "7.16.0",
|
||||||
|
"react-is": "^19.1.1",
|
||||||
"rollup@>=4.0.0 <4.22.4": ">=4.50.1",
|
"rollup@>=4.0.0 <4.22.4": ">=4.50.1",
|
||||||
"sha.js@<=2.4.11": ">=2.4.12",
|
"sha.js@<=2.4.11": ">=2.4.12",
|
||||||
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.0",
|
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.0",
|
||||||
|
|||||||
@@ -2503,6 +2503,9 @@
|
|||||||
"name": "System resources",
|
"name": "System resources",
|
||||||
"description": "CPU, Memory, Disk and other hardware usage of your system",
|
"description": "CPU, Memory, Disk and other hardware usage of your system",
|
||||||
"option": {
|
"option": {
|
||||||
|
"hasShadow": {
|
||||||
|
"label": "Enable chart shading"
|
||||||
|
},
|
||||||
"visibleCharts": {
|
"visibleCharts": {
|
||||||
"label": "Visible charts",
|
"label": "Visible charts",
|
||||||
"description": "Select the charts you want to be visible.",
|
"description": "Select the charts you want to be visible.",
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ import { CommonChart } from "./common-chart";
|
|||||||
|
|
||||||
export const CombinedNetworkTrafficChart = ({
|
export const CombinedNetworkTrafficChart = ({
|
||||||
usageOverTime,
|
usageOverTime,
|
||||||
|
hasShadow,
|
||||||
labelDisplayMode,
|
labelDisplayMode,
|
||||||
}: {
|
}: {
|
||||||
usageOverTime: {
|
usageOverTime: {
|
||||||
up: number;
|
up: number;
|
||||||
down: number;
|
down: number;
|
||||||
}[];
|
}[];
|
||||||
|
hasShadow: boolean;
|
||||||
labelDisplayMode: LabelDisplayModeOption;
|
labelDisplayMode: LabelDisplayModeOption;
|
||||||
}) => {
|
}) => {
|
||||||
const chartData = usageOverTime.map((usage, index) => ({ index, up: usage.up, down: usage.down }));
|
const chartData = usageOverTime.map((usage, index) => ({ index, up: usage.up, down: usage.down }));
|
||||||
@@ -31,6 +33,7 @@ export const CombinedNetworkTrafficChart = ({
|
|||||||
title={t("network")}
|
title={t("network")}
|
||||||
icon={IconNetwork}
|
icon={IconNetwork}
|
||||||
yAxisProps={{ domain: [0, "dataMax"] }}
|
yAxisProps={{ domain: [0, "dataMax"] }}
|
||||||
|
chartType={hasShadow ? "area" : "line"}
|
||||||
labelDisplayMode={labelDisplayMode}
|
labelDisplayMode={labelDisplayMode}
|
||||||
tooltipProps={{
|
tooltipProps={{
|
||||||
content: ({ payload }) => {
|
content: ({ payload }) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import type { LineChartSeries } from "@mantine/charts";
|
import type { AreaChartSeries } from "@mantine/charts";
|
||||||
import { LineChart } from "@mantine/charts";
|
import { AreaChart, LineChart } from "@mantine/charts";
|
||||||
import { Card, Center, Group, Loader, Stack, Text, useMantineColorScheme, useMantineTheme } from "@mantine/core";
|
import { Card, Center, Group, Loader, Stack, Text, useMantineColorScheme, useMantineTheme } from "@mantine/core";
|
||||||
import { useElementSize, useHover, useMergedRef } from "@mantine/hooks";
|
import { useElementSize, useHover, useMergedRef } from "@mantine/hooks";
|
||||||
import type { TooltipProps, YAxisProps } from "recharts";
|
import type { TooltipProps, YAxisProps } from "recharts";
|
||||||
@@ -21,16 +21,18 @@ export const CommonChart = ({
|
|||||||
tooltipProps,
|
tooltipProps,
|
||||||
yAxisProps,
|
yAxisProps,
|
||||||
lastValue,
|
lastValue,
|
||||||
|
chartType = "line",
|
||||||
}: {
|
}: {
|
||||||
data: Record<string, any>[];
|
data: Record<string, any>[];
|
||||||
dataKey: string;
|
dataKey: string;
|
||||||
series: LineChartSeries[];
|
series: AreaChartSeries[];
|
||||||
title: ReactNode;
|
title: ReactNode;
|
||||||
icon: TablerIcon;
|
icon: TablerIcon;
|
||||||
labelDisplayMode: LabelDisplayModeOption;
|
labelDisplayMode: LabelDisplayModeOption;
|
||||||
tooltipProps?: TooltipProps<number, any>;
|
tooltipProps?: TooltipProps<number, any>;
|
||||||
yAxisProps?: Omit<YAxisProps, "ref">;
|
yAxisProps?: Omit<YAxisProps, "ref">;
|
||||||
lastValue?: string;
|
lastValue?: string;
|
||||||
|
chartType?: "line" | "area";
|
||||||
}) => {
|
}) => {
|
||||||
const { ref: elementSizeRef, height } = useElementSize();
|
const { ref: elementSizeRef, height } = useElementSize();
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
@@ -43,6 +45,7 @@ export const CommonChart = ({
|
|||||||
const backgroundColor =
|
const backgroundColor =
|
||||||
scheme.colorScheme === "dark" ? `rgba(57, 57, 57, ${opacity})` : `rgba(246, 247, 248, ${opacity})`;
|
scheme.colorScheme === "dark" ? `rgba(57, 57, 57, ${opacity})` : `rgba(246, 247, 248, ${opacity})`;
|
||||||
|
|
||||||
|
const ChartComponent = chartType === "line" ? LineChart : AreaChart;
|
||||||
const showIcon = labelDisplayMode === "icon" || labelDisplayMode === "textWithIcon";
|
const showIcon = labelDisplayMode === "icon" || labelDisplayMode === "textWithIcon";
|
||||||
const showText = labelDisplayMode === "text" || labelDisplayMode === "textWithIcon";
|
const showText = labelDisplayMode === "text" || labelDisplayMode === "textWithIcon";
|
||||||
|
|
||||||
@@ -88,7 +91,7 @@ export const CommonChart = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
<LineChart
|
<ChartComponent
|
||||||
data={data}
|
data={data}
|
||||||
dataKey={dataKey}
|
dataKey={dataKey}
|
||||||
h={"100%"}
|
h={"100%"}
|
||||||
@@ -105,6 +108,7 @@ export const CommonChart = ({
|
|||||||
tooltipProps={tooltipProps}
|
tooltipProps={tooltipProps}
|
||||||
withTooltip={height >= 64}
|
withTooltip={height >= 64}
|
||||||
yAxisProps={yAxisProps}
|
yAxisProps={yAxisProps}
|
||||||
|
fillOpacity={chartType === "area" ? 0.3 : undefined}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ import { IconCpu } from "@tabler/icons-react";
|
|||||||
|
|
||||||
import { useScopedI18n } from "@homarr/translation/client";
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
import { LabelDisplayModeOption } from "..";
|
import type { LabelDisplayModeOption } from "..";
|
||||||
import { CommonChart } from "./common-chart";
|
import { CommonChart } from "./common-chart";
|
||||||
|
|
||||||
export const SystemResourceCPUChart = ({
|
export const SystemResourceCPUChart = ({
|
||||||
cpuUsageOverTime,
|
cpuUsageOverTime,
|
||||||
|
hasShadow,
|
||||||
labelDisplayMode,
|
labelDisplayMode,
|
||||||
}: {
|
}: {
|
||||||
cpuUsageOverTime: number[];
|
cpuUsageOverTime: number[];
|
||||||
|
hasShadow: boolean;
|
||||||
labelDisplayMode: LabelDisplayModeOption;
|
labelDisplayMode: LabelDisplayModeOption;
|
||||||
}) => {
|
}) => {
|
||||||
const chartData = cpuUsageOverTime.map((usage, index) => ({ index, usage }));
|
const chartData = cpuUsageOverTime.map((usage, index) => ({ index, usage }));
|
||||||
@@ -27,6 +29,7 @@ export const SystemResourceCPUChart = ({
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
cpuUsageOverTime.length > 0 ? `${Math.round(cpuUsageOverTime[cpuUsageOverTime.length - 1]!)}%` : undefined
|
cpuUsageOverTime.length > 0 ? `${Math.round(cpuUsageOverTime[cpuUsageOverTime.length - 1]!)}%` : undefined
|
||||||
}
|
}
|
||||||
|
chartType={hasShadow ? "area" : "line"}
|
||||||
yAxisProps={{ domain: [0, 100] }}
|
yAxisProps={{ domain: [0, 100] }}
|
||||||
labelDisplayMode={labelDisplayMode}
|
labelDisplayMode={labelDisplayMode}
|
||||||
tooltipProps={{
|
tooltipProps={{
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import { CommonChart } from "./common-chart";
|
|||||||
export const SystemResourceMemoryChart = ({
|
export const SystemResourceMemoryChart = ({
|
||||||
memoryUsageOverTime,
|
memoryUsageOverTime,
|
||||||
totalCapacityInBytes,
|
totalCapacityInBytes,
|
||||||
|
hasShadow,
|
||||||
labelDisplayMode,
|
labelDisplayMode,
|
||||||
}: {
|
}: {
|
||||||
memoryUsageOverTime: number[];
|
memoryUsageOverTime: number[];
|
||||||
totalCapacityInBytes: number;
|
totalCapacityInBytes: number;
|
||||||
|
hasShadow: boolean;
|
||||||
labelDisplayMode: LabelDisplayModeOption;
|
labelDisplayMode: LabelDisplayModeOption;
|
||||||
}) => {
|
}) => {
|
||||||
const chartData = memoryUsageOverTime.map((usage, index) => ({ index, usage }));
|
const chartData = memoryUsageOverTime.map((usage, index) => ({ index, usage }));
|
||||||
@@ -35,6 +37,7 @@ export const SystemResourceMemoryChart = ({
|
|||||||
labelDisplayMode={labelDisplayMode}
|
labelDisplayMode={labelDisplayMode}
|
||||||
yAxisProps={{ domain: [0, totalCapacityInBytes] }}
|
yAxisProps={{ domain: [0, totalCapacityInBytes] }}
|
||||||
lastValue={percentageUsed !== undefined ? `${Math.round(percentageUsed * 100)}%` : undefined}
|
lastValue={percentageUsed !== undefined ? `${Math.round(percentageUsed * 100)}%` : undefined}
|
||||||
|
chartType={hasShadow ? "area" : "line"}
|
||||||
tooltipProps={{
|
tooltipProps={{
|
||||||
content: ({ payload }) => {
|
content: ({ payload }) => {
|
||||||
if (!payload) {
|
if (!payload) {
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import { CommonChart } from "./common-chart";
|
|||||||
export const NetworkTrafficChart = ({
|
export const NetworkTrafficChart = ({
|
||||||
usageOverTime,
|
usageOverTime,
|
||||||
isUp,
|
isUp,
|
||||||
|
hasShadow,
|
||||||
labelDisplayMode,
|
labelDisplayMode,
|
||||||
}: {
|
}: {
|
||||||
usageOverTime: number[];
|
usageOverTime: number[];
|
||||||
isUp: boolean;
|
isUp: boolean;
|
||||||
|
hasShadow: boolean;
|
||||||
labelDisplayMode: LabelDisplayModeOption;
|
labelDisplayMode: LabelDisplayModeOption;
|
||||||
}) => {
|
}) => {
|
||||||
const chartData = usageOverTime.map((usage, index) => ({ index, usage }));
|
const chartData = usageOverTime.map((usage, index) => ({ index, usage }));
|
||||||
@@ -31,6 +33,7 @@ export const NetworkTrafficChart = ({
|
|||||||
icon={isUp ? IconArrowUp : IconArrowDown}
|
icon={isUp ? IconArrowUp : IconArrowDown}
|
||||||
yAxisProps={{ domain: [0, upperBound] }}
|
yAxisProps={{ domain: [0, upperBound] }}
|
||||||
lastValue={`${humanFileSize(Math.round(max))}/s`}
|
lastValue={`${humanFileSize(Math.round(max))}/s`}
|
||||||
|
chartType={hasShadow ? "area" : "line"}
|
||||||
labelDisplayMode={labelDisplayMode}
|
labelDisplayMode={labelDisplayMode}
|
||||||
tooltipProps={{
|
tooltipProps={{
|
||||||
content: ({ payload }) => {
|
content: ({ payload }) => {
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export default function SystemResources({ integrationIds, options }: WidgetCompo
|
|||||||
<Box h={rowHeight}>
|
<Box h={rowHeight}>
|
||||||
<SystemResourceCPUChart
|
<SystemResourceCPUChart
|
||||||
cpuUsageOverTime={items.map((item) => item.cpu)}
|
cpuUsageOverTime={items.map((item) => item.cpu)}
|
||||||
|
hasShadow={options.hasShadow}
|
||||||
labelDisplayMode={options.labelDisplayMode}
|
labelDisplayMode={options.labelDisplayMode}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -68,6 +69,7 @@ export default function SystemResources({ integrationIds, options }: WidgetCompo
|
|||||||
<SystemResourceMemoryChart
|
<SystemResourceMemoryChart
|
||||||
memoryUsageOverTime={items.map((item) => item.memory)}
|
memoryUsageOverTime={items.map((item) => item.memory)}
|
||||||
totalCapacityInBytes={memoryCapacityInBytes}
|
totalCapacityInBytes={memoryCapacityInBytes}
|
||||||
|
hasShadow={options.hasShadow}
|
||||||
labelDisplayMode={options.labelDisplayMode}
|
labelDisplayMode={options.labelDisplayMode}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -79,6 +81,7 @@ export default function SystemResources({ integrationIds, options }: WidgetCompo
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
usageOverTime={items.map((item) => item.network!.down)}
|
usageOverTime={items.map((item) => item.network!.down)}
|
||||||
isUp={false}
|
isUp={false}
|
||||||
|
hasShadow={options.hasShadow}
|
||||||
labelDisplayMode={options.labelDisplayMode}
|
labelDisplayMode={options.labelDisplayMode}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -86,6 +89,7 @@ export default function SystemResources({ integrationIds, options }: WidgetCompo
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
usageOverTime={items.map((item) => item.network!.up)}
|
usageOverTime={items.map((item) => item.network!.up)}
|
||||||
isUp
|
isUp
|
||||||
|
hasShadow={options.hasShadow}
|
||||||
labelDisplayMode={options.labelDisplayMode}
|
labelDisplayMode={options.labelDisplayMode}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -94,6 +98,7 @@ export default function SystemResources({ integrationIds, options }: WidgetCompo
|
|||||||
<CombinedNetworkTrafficChart
|
<CombinedNetworkTrafficChart
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
usageOverTime={items.map((item) => item.network!)}
|
usageOverTime={items.map((item) => item.network!)}
|
||||||
|
hasShadow={options.hasShadow}
|
||||||
labelDisplayMode={options.labelDisplayMode}
|
labelDisplayMode={options.labelDisplayMode}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export const { definition, componentLoader } = createWidgetDefinition("systemRes
|
|||||||
supportedIntegrations: ["dashDot", "openmediavault", "truenas"],
|
supportedIntegrations: ["dashDot", "openmediavault", "truenas"],
|
||||||
createOptions() {
|
createOptions() {
|
||||||
return optionsBuilder.from((factory) => ({
|
return optionsBuilder.from((factory) => ({
|
||||||
|
hasShadow: factory.switch({ defaultValue: true }),
|
||||||
visibleCharts: factory.multiSelect({
|
visibleCharts: factory.multiSelect({
|
||||||
options: (["cpu", "memory", "network"] as const).map((key) => ({
|
options: (["cpu", "memory", "network"] as const).map((key) => ({
|
||||||
value: key,
|
value: key,
|
||||||
|
|||||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -17,6 +17,7 @@ overrides:
|
|||||||
nanoid@>=4.0.0 <5.0.9: '>=5.1.5'
|
nanoid@>=4.0.0 <5.0.9: '>=5.1.5'
|
||||||
prismjs@<1.30.0: '>=1.30.0'
|
prismjs@<1.30.0: '>=1.30.0'
|
||||||
proxmox-api>undici: 7.16.0
|
proxmox-api>undici: 7.16.0
|
||||||
|
react-is: ^19.1.1
|
||||||
rollup@>=4.0.0 <4.22.4: '>=4.50.1'
|
rollup@>=4.0.0 <4.22.4: '>=4.50.1'
|
||||||
sha.js@<=2.4.11: '>=2.4.12'
|
sha.js@<=2.4.11: '>=2.4.12'
|
||||||
tar-fs@>=3.0.0 <3.0.9: '>=3.1.0'
|
tar-fs@>=3.0.0 <3.0.9: '>=3.1.0'
|
||||||
@@ -8897,11 +8898,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.8.4 || ^17.0.0 || ^18.0.0
|
react: ^16.8.4 || ^17.0.0 || ^18.0.0
|
||||||
|
|
||||||
react-is@16.13.1:
|
react-is@19.1.1:
|
||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
resolution: {integrity: sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==}
|
||||||
|
|
||||||
react-is@18.3.1:
|
|
||||||
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
|
||||||
|
|
||||||
react-markdown@10.1.0:
|
react-markdown@10.1.0:
|
||||||
resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==}
|
resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==}
|
||||||
@@ -17874,7 +17872,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
react-is: 16.13.1
|
react-is: 19.1.1
|
||||||
|
|
||||||
proper-lockfile@4.1.2:
|
proper-lockfile@4.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -18133,9 +18131,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react: 19.1.1
|
react: 19.1.1
|
||||||
|
|
||||||
react-is@16.13.1: {}
|
react-is@19.1.1: {}
|
||||||
|
|
||||||
react-is@18.3.1: {}
|
|
||||||
|
|
||||||
react-markdown@10.1.0(@types/react@19.1.13)(react@19.1.1):
|
react-markdown@10.1.0(@types/react@19.1.13)(react@19.1.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -18314,7 +18310,7 @@ snapshots:
|
|||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
react: 19.1.1
|
react: 19.1.1
|
||||||
react-dom: 19.1.1(react@19.1.1)
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
react-is: 18.3.1
|
react-is: 19.1.1
|
||||||
react-smooth: 4.0.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
react-smooth: 4.0.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
recharts-scale: 0.4.5
|
recharts-scale: 0.4.5
|
||||||
tiny-invariant: 1.3.3
|
tiny-invariant: 1.3.3
|
||||||
|
|||||||
Reference in New Issue
Block a user