frontend/src/charts/WaterLevelChart.tsx

136 lines
4 KiB
TypeScript

import { useTheme } from "@mui/material";
import { blue } from "@mui/material/colors";
import moment from "moment";
import React, { useEffect, useState } from "react";
import {
Area,
AreaChart,
ReferenceArea,
ReferenceLine,
ResponsiveContainer,
Tooltip,
TooltipProps,
XAxis,
YAxis
} from "recharts";
import MaterialChartTooltip from "./MaterialChartTooltip";
export interface DataPoint {
timestamp: number;
liters: number;
}
interface Props {
data: DataPoint[];
reference: number;
newXDomain?: number[] | string[];
multiGraphZoom?: (domain: number[] | string[]) => void;
multiGraphZoomOut?: boolean;
}
export default function WaterLevelChart(props: Props) {
const { data, reference, newXDomain, multiGraphZoom } = props;
const theme = useTheme();
const now = moment();
const [xDomain, setXDomain] = useState<string[] | number[]>(["dataMin", "dataMax"]);
const [refLeft, setRefLeft] = useState<number | undefined>();
const [refRight, setRefRight] = useState<number | undefined>();
useEffect(() => {
if (newXDomain) {
setXDomain(newXDomain);
}
}, [newXDomain]);
const zoom = () => {
let newDomain: number[] | string[] = ["dataMin", "dataMax"];
if (refLeft && refRight && refLeft !== refRight) {
refLeft < refRight ? (newDomain = [refLeft, refRight]) : (newDomain = [refRight, refLeft]);
setRefLeft(undefined);
setRefRight(undefined);
if (multiGraphZoom) {
multiGraphZoom(newDomain);
} else {
setXDomain(newDomain);
}
}
};
//just commenting this out as this charts zoom will likely never be controlled from inside
// const zoomOut = () => {
// setXDomain(["dataMin", "dataMax"]);
// };
return (
<React.Fragment>
<ResponsiveContainer width={"100%"} height={270}>
<AreaChart
data={data}
onMouseDown={(e: any) => {
if (e) {
setRefLeft(e.activeLabel);
}
}}
onMouseMove={(e: any) => {
if (e) {
setRefRight(e.activeLabel);
}
}}
onMouseUp={() => {
setRefLeft(undefined);
setRefRight(undefined);
zoom();
}}>
<Tooltip
animationEasing="ease-out"
cursor={{ fill: theme.palette.text.primary, opacity: "0.15" }}
labelFormatter={timestamp => moment(timestamp).format("lll")}
content={(props: TooltipProps<any, any>) => (
<MaterialChartTooltip {...props} valueFormatter={liters => `${liters}`} />
)}
/>
<XAxis
allowDataOverflow
dataKey="timestamp"
domain={xDomain}
name="Time"
tickFormatter={timestamp => {
let t = moment(timestamp);
return now.isSame(t, "day") ? t.format("LT") : t.format("MMM DD");
}}
scale="time"
type="number"
tick={{ fill: theme.palette.text.primary }}
stroke={theme.palette.divider}
interval="preserveStartEnd"
/>
<YAxis
tickFormatter={liters => {
let val = liters / 1000 + "kL";
return val;
}}
type="number"
// label={{
// value: "Water Content",
// angle: -90,
// position: "insideLeft",
// fill: theme.palette.text.primary
// }}
domain={["auto", "auto"]}
tick={{ fill: theme.palette.text.primary }}
/>
<Area dataKey={"liters"} fill={blue[500]} />
<ReferenceLine
ifOverflow="extendDomain"
y={reference}
stroke="red"
strokeDasharray={"5 2"}
/>
{refLeft && refRight ? (
<ReferenceArea x1={refLeft} x2={refRight} strokeOpacity={0.3} />
) : null}
</AreaChart>
</ResponsiveContainer>
</React.Fragment>
);
}