136 lines
4 KiB
TypeScript
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>
|
|
);
|
|
}
|