Files
homarr/packages/widgets/src/system-resources/chart/common-chart.tsx
T
Bergasha 73de83b48f Address CodeRabbit review on system resources widget PR
- Render the chart tooltip via a portal so it isn't clipped by the
  card's overflow:hidden (needed to stop the chart line itself
  bleeding past its rounded corners).
- Move chartColumns' width breakpoints into a lookup table and
  memoize the computed value, instead of an inline ternary.
2026-08-27 10:46:04 +10:00

144 lines
4.7 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-explicit-any */
import type { ReactNode } from "react";
import type { AreaChartSeries } from "@mantine/charts";
import { AreaChart, LineChart } from "@mantine/charts";
import { Card, Center, Group, Stack, Text, useMantineTheme } from "@mantine/core";
import { useElementSize, useHover, useMergedRef } from "@mantine/hooks";
import type { TooltipProps, YAxisProps } from "recharts";
import { useRequiredBoard } from "@homarr/boards/context";
import { zoomCompensatedSize } from "@homarr/ui";
import type { TablerIcon } from "@homarr/ui";
import type { LabelDisplayModeOption } from "..";
export const CommonChart = ({
data,
dataKey,
series,
title,
icon: Icon,
labelDisplayMode,
tooltipProps,
yAxisProps,
lastValue,
chartType = "line",
}: {
data: Record<string, any>[];
dataKey: string;
series: AreaChartSeries[];
title: ReactNode;
icon: TablerIcon;
labelDisplayMode: LabelDisplayModeOption;
tooltipProps?: TooltipProps<number, any>;
yAxisProps?: Omit<YAxisProps, "ref">;
lastValue?: string;
chartType?: "line" | "area";
}) => {
const { ref: elementSizeRef, height } = useElementSize();
const theme = useMantineTheme();
const board = useRequiredBoard();
const { hovered, ref: hoverRef } = useHover();
const ref = useMergedRef(elementSizeRef, hoverRef);
const backgroundColor = "rgb(from var(--mantine-color-primaryColor-filled) r g b / calc(var(--opacity, 1) * 0.12))";
const borderColor = "rgb(from var(--mantine-color-secondaryColor-filled) r g b / calc(var(--opacity, 1) * 0.45))";
const ChartComponent = chartType === "line" ? LineChart : AreaChart;
const showIcon = labelDisplayMode === "icon" || labelDisplayMode === "textWithIcon";
const showText = labelDisplayMode === "text" || labelDisplayMode === "textWithIcon";
return (
<Card
ref={ref}
h={"100%"}
pos={"relative"}
style={{ overflow: "hidden", border: `1px solid ${borderColor}` }}
p={0}
bg={backgroundColor}
radius={board.itemRadius}
>
{data.length > 1 && height > 40 && !hovered && (
<Group
pos={"absolute"}
top={0}
left={0}
p={8}
pt={6}
gap={5}
wrap={"nowrap"}
style={{ zIndex: 2, pointerEvents: "none" }}
align="center"
>
{showIcon && <Icon color={"var(--mantine-color-dimmed)"} style={zoomCompensatedSize(height > 100 ? 20 : 14)} stroke={1.5} />}
{showText && (
<Text c={"dimmed"} size={height > 100 ? "md" : "xs"} fw={"bold"}>
{title}
</Text>
)}
{lastValue && (
<Text c={"dimmed"} size={height > 100 ? "md" : "xs"} lineClamp={1}>
{lastValue}
</Text>
)}
</Group>
)}
{data.length > 1 && height > 0 && height <= 40 && lastValue && !hovered && (
<Center pos="absolute" w="100%" h="100%" style={{ zIndex: 2, pointerEvents: "none" }}>
<Text size="xs" fw={600}>
{lastValue}
</Text>
</Center>
)}
{data.length <= 1 ? (
<Center pos="absolute" w="100%" h="100%">
<Stack px={"xs"} align={"center"} gap={4}>
{showIcon && <Icon color={"var(--mantine-color-dimmed)"} style={zoomCompensatedSize(height > 100 ? 20 : 14)} stroke={1.5} />}
{showText && (
<Text c={"dimmed"} size={height > 100 ? "md" : "xs"} fw={"bold"} ta="center">
{title}
</Text>
)}
{lastValue && (
<Text size={height > 100 ? "md" : "xs"} fw={600} ta="center">
{lastValue}
</Text>
)}
</Stack>
</Center>
) : (
<ChartComponent
data={data}
dataKey={dataKey}
h={"100%"}
series={series}
curveType="monotone"
tickLine="none"
gridAxis="none"
withXAxis={false}
withYAxis={false}
withDots={false}
bg={backgroundColor}
styles={{
root: {
padding: 5,
borderRadius: theme.radius[board.itemRadius],
backgroundColor,
},
}}
tooltipAnimationDuration={200}
tooltipProps={{
// Render via portal so the tooltip isn't clipped by the card's overflow:hidden
// (needed to stop the chart line itself bleeding past its rounded corners).
portal: typeof document !== "undefined" ? document.body : undefined,
...tooltipProps,
}}
withTooltip={height >= 64}
yAxisProps={yAxisProps}
fillOpacity={chartType === "area" ? 0.3 : undefined}
/>
)}
</Card>
);
};