diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 255e716ab..edab3eef8 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -2511,6 +2511,15 @@ "memory": "Memory", "network": "Network" } + }, + "labelDisplayMode": { + "label": "Label display mode", + "option": { + "textWithIcon": "Show text with icon", + "text": "Show only text", + "icon": "Show only icon", + "hidden": "Hide label" + } } }, "card": { diff --git a/packages/widgets/src/system-resources/chart/combined-network-traffic.tsx b/packages/widgets/src/system-resources/chart/combined-network-traffic.tsx index d2db770da..a9b254b79 100644 --- a/packages/widgets/src/system-resources/chart/combined-network-traffic.tsx +++ b/packages/widgets/src/system-resources/chart/combined-network-traffic.tsx @@ -1,17 +1,21 @@ import { Box, Group, Paper, Stack, Text } from "@mantine/core"; +import { IconNetwork } from "@tabler/icons-react"; import { humanFileSize } from "@homarr/common"; import { useScopedI18n } from "@homarr/translation/client"; +import type { LabelDisplayModeOption } from ".."; import { CommonChart } from "./common-chart"; export const CombinedNetworkTrafficChart = ({ usageOverTime, + labelDisplayMode, }: { usageOverTime: { up: number; down: number; }[]; + labelDisplayMode: LabelDisplayModeOption; }) => { const chartData = usageOverTime.map((usage, index) => ({ index, up: usage.up, down: usage.down })); const t = useScopedI18n("widget.systemResources.card"); @@ -25,7 +29,9 @@ export const CombinedNetworkTrafficChart = ({ { name: "down", color: "yellow.5" }, ]} title={t("network")} + icon={IconNetwork} yAxisProps={{ domain: [0, "dataMax"] }} + labelDisplayMode={labelDisplayMode} tooltipProps={{ content: ({ payload }) => { if (!payload) { diff --git a/packages/widgets/src/system-resources/chart/common-chart.tsx b/packages/widgets/src/system-resources/chart/common-chart.tsx index 40760abdd..833d7007c 100644 --- a/packages/widgets/src/system-resources/chart/common-chart.tsx +++ b/packages/widgets/src/system-resources/chart/common-chart.tsx @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { ReactNode } from "react"; import type { LineChartSeries } from "@mantine/charts"; import { LineChart } from "@mantine/charts"; import { Card, Center, Group, Loader, Stack, Text, useMantineColorScheme, useMantineTheme } from "@mantine/core"; @@ -6,12 +7,17 @@ import { useElementSize, useHover, useMergedRef } from "@mantine/hooks"; import type { TooltipProps, YAxisProps } from "recharts"; import { useRequiredBoard } from "@homarr/boards/context"; +import type { TablerIcon } from "@homarr/ui"; + +import type { LabelDisplayModeOption } from ".."; export const CommonChart = ({ data, dataKey, series, title, + icon: Icon, + labelDisplayMode, tooltipProps, yAxisProps, lastValue, @@ -19,7 +25,9 @@ export const CommonChart = ({ data: Record[]; dataKey: string; series: LineChartSeries[]; - title: string; + title: ReactNode; + icon: TablerIcon; + labelDisplayMode: LabelDisplayModeOption; tooltipProps?: TooltipProps; yAxisProps?: Omit; lastValue?: string; @@ -35,6 +43,9 @@ export const CommonChart = ({ const backgroundColor = scheme.colorScheme === "dark" ? `rgba(57, 57, 57, ${opacity})` : `rgba(246, 247, 248, ${opacity})`; + const showIcon = labelDisplayMode === "icon" || labelDisplayMode === "textWithIcon"; + const showText = labelDisplayMode === "text" || labelDisplayMode === "textWithIcon"; + return ( - 100 ? "md" : "xs"} fw={"bold"}> - {title} - + {showIcon && 100 ? 20 : 14} stroke={1.5} />} + {showText && ( + 100 ? "md" : "xs"} fw={"bold"}> + {title} + + )} {lastValue && ( 100 ? "md" : "xs"} lineClamp={1}> {lastValue} diff --git a/packages/widgets/src/system-resources/chart/cpu-chart.tsx b/packages/widgets/src/system-resources/chart/cpu-chart.tsx index 0e5f81e81..3085f5b6d 100644 --- a/packages/widgets/src/system-resources/chart/cpu-chart.tsx +++ b/packages/widgets/src/system-resources/chart/cpu-chart.tsx @@ -1,10 +1,18 @@ import { Paper, Text } from "@mantine/core"; +import { IconCpu } from "@tabler/icons-react"; import { useScopedI18n } from "@homarr/translation/client"; +import { LabelDisplayModeOption } from ".."; import { CommonChart } from "./common-chart"; -export const SystemResourceCPUChart = ({ cpuUsageOverTime }: { cpuUsageOverTime: number[] }) => { +export const SystemResourceCPUChart = ({ + cpuUsageOverTime, + labelDisplayMode, +}: { + cpuUsageOverTime: number[]; + labelDisplayMode: LabelDisplayModeOption; +}) => { const chartData = cpuUsageOverTime.map((usage, index) => ({ index, usage })); const t = useScopedI18n("widget.systemResources.card"); @@ -14,11 +22,13 @@ export const SystemResourceCPUChart = ({ cpuUsageOverTime }: { cpuUsageOverTime: dataKey={"index"} series={[{ name: "usage", color: "blue.5" }]} title={t("cpu")} + icon={IconCpu} lastValue={ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion cpuUsageOverTime.length > 0 ? `${Math.round(cpuUsageOverTime[cpuUsageOverTime.length - 1]!)}%` : undefined } yAxisProps={{ domain: [0, 100] }} + labelDisplayMode={labelDisplayMode} tooltipProps={{ content: ({ payload }) => { if (!payload) { diff --git a/packages/widgets/src/system-resources/chart/memory-chart.tsx b/packages/widgets/src/system-resources/chart/memory-chart.tsx index 026128268..08a15a997 100644 --- a/packages/widgets/src/system-resources/chart/memory-chart.tsx +++ b/packages/widgets/src/system-resources/chart/memory-chart.tsx @@ -1,16 +1,20 @@ import { Paper, Text } from "@mantine/core"; +import { IconBrain } from "@tabler/icons-react"; import { humanFileSize } from "@homarr/common"; import { useScopedI18n } from "@homarr/translation/client"; +import type { LabelDisplayModeOption } from ".."; import { CommonChart } from "./common-chart"; export const SystemResourceMemoryChart = ({ memoryUsageOverTime, totalCapacityInBytes, + labelDisplayMode, }: { memoryUsageOverTime: number[]; totalCapacityInBytes: number; + labelDisplayMode: LabelDisplayModeOption; }) => { const chartData = memoryUsageOverTime.map((usage, index) => ({ index, usage })); const t = useScopedI18n("widget.systemResources.card"); @@ -27,6 +31,8 @@ export const SystemResourceMemoryChart = ({ dataKey={"index"} series={[{ name: "usage", color: "red.6" }]} title={t("memory")} + icon={IconBrain} + labelDisplayMode={labelDisplayMode} yAxisProps={{ domain: [0, totalCapacityInBytes] }} lastValue={percentageUsed !== undefined ? `${Math.round(percentageUsed * 100)}%` : undefined} tooltipProps={{ diff --git a/packages/widgets/src/system-resources/chart/network-traffic.tsx b/packages/widgets/src/system-resources/chart/network-traffic.tsx index 91e9a201d..70452fadf 100644 --- a/packages/widgets/src/system-resources/chart/network-traffic.tsx +++ b/packages/widgets/src/system-resources/chart/network-traffic.tsx @@ -1,11 +1,21 @@ import { Paper, Text } from "@mantine/core"; +import { IconArrowDown, IconArrowUp } from "@tabler/icons-react"; import { humanFileSize } from "@homarr/common"; import { useScopedI18n } from "@homarr/translation/client"; +import type { LabelDisplayModeOption } from ".."; import { CommonChart } from "./common-chart"; -export const NetworkTrafficChart = ({ usageOverTime, isUp }: { usageOverTime: number[]; isUp: boolean }) => { +export const NetworkTrafficChart = ({ + usageOverTime, + isUp, + labelDisplayMode, +}: { + usageOverTime: number[]; + isUp: boolean; + labelDisplayMode: LabelDisplayModeOption; +}) => { const chartData = usageOverTime.map((usage, index) => ({ index, usage })); const t = useScopedI18n("widget.systemResources.card"); @@ -18,8 +28,10 @@ export const NetworkTrafficChart = ({ usageOverTime, isUp }: { usageOverTime: nu dataKey={"index"} series={[{ name: "usage", color: "yellow.5" }]} title={isUp ? t("up") : t("down")} + icon={isUp ? IconArrowUp : IconArrowDown} yAxisProps={{ domain: [0, upperBound] }} lastValue={`${humanFileSize(Math.round(max))}/s`} + labelDisplayMode={labelDisplayMode} tooltipProps={{ content: ({ payload }) => { if (!payload) { diff --git a/packages/widgets/src/system-resources/component.tsx b/packages/widgets/src/system-resources/component.tsx index 648f91da6..654436705 100644 --- a/packages/widgets/src/system-resources/component.tsx +++ b/packages/widgets/src/system-resources/component.tsx @@ -57,7 +57,10 @@ export default function SystemResources({ integrationIds, options }: WidgetCompo {options.visibleCharts.includes("cpu") && ( - item.cpu)} /> + item.cpu)} + labelDisplayMode={options.labelDisplayMode} + /> )} {options.visibleCharts.includes("memory") && ( @@ -65,22 +68,34 @@ export default function SystemResources({ integrationIds, options }: WidgetCompo item.memory)} totalCapacityInBytes={memoryCapacityInBytes} + labelDisplayMode={options.labelDisplayMode} /> )} {showNetwork && (width > 256 ? ( - {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} - item.network!.down)} isUp={false} /> + item.network!.down)} + isUp={false} + labelDisplayMode={options.labelDisplayMode} + /> - {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} - item.network!.up)} isUp /> + item.network!.up)} + isUp + labelDisplayMode={options.labelDisplayMode} + /> ) : ( - {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} - item.network!)} /> + item.network!)} + labelDisplayMode={options.labelDisplayMode} + /> ))} diff --git a/packages/widgets/src/system-resources/index.ts b/packages/widgets/src/system-resources/index.ts index f6115dce2..13b206020 100644 --- a/packages/widgets/src/system-resources/index.ts +++ b/packages/widgets/src/system-resources/index.ts @@ -1,8 +1,17 @@ -import { IconGraphFilled } from "@tabler/icons-react"; +import { IconAlignLeft, IconEyeOff, IconGraphFilled, IconListDetails, IconPhoto } from "@tabler/icons-react"; + +import { objectEntries } from "@homarr/common"; import { createWidgetDefinition } from "../definition"; import { optionsBuilder } from "../options"; +const labelDisplayModeOptions = { + textWithIcon: IconListDetails, + text: IconAlignLeft, + icon: IconPhoto, + hidden: IconEyeOff, +} as const; + export const { definition, componentLoader } = createWidgetDefinition("systemResources", { icon: IconGraphFilled, supportedIntegrations: ["dashDot", "openmediavault", "truenas"], @@ -16,6 +25,18 @@ export const { definition, componentLoader } = createWidgetDefinition("systemRes defaultValue: ["cpu", "memory", "network"], withDescription: true, }), + labelDisplayMode: factory.select({ + options: objectEntries(labelDisplayModeOptions).map(([key, icon]) => ({ + value: key, + label: (t) => t(`widget.systemResources.option.labelDisplayMode.option.${key}`), + icon, + })), + defaultValue: "textWithIcon", + }), })); }, }).withDynamicImport(() => import("./component")); + +export type LabelDisplayModeOption = ReturnType< + (typeof definition)["createOptions"] +>["labelDisplayMode"]["options"][number]["value"];