Skip to content

Commit

Permalink
More ShotChart fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
kstam committed Jul 10, 2023
1 parent ebff847 commit ea7078c
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 57 deletions.
11 changes: 10 additions & 1 deletion webserver/web-interface/src/components/chart/ChartConfig.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Theme, alpha } from '@mui/material';
import { ChartOptions } from 'chart.js';

export function getShotChartConfig(theme: Theme): ChartOptions<'line'> {
export function getShotChartConfig(theme: Theme, onHover?: (index: number) => void): ChartOptions<'line'> {
return {
animation: false,
responsive: true,
maintainAspectRatio: false,
onHover: (event, elements) => {
if (onHover && elements && elements.length > 0 && elements[0].index) {
const { index } = elements[0]; // get the index of the hovered element
onHover(index);
}
},
interaction: {
mode: 'index',
intersect: false,
Expand All @@ -14,6 +20,9 @@ export function getShotChartConfig(theme: Theme): ChartOptions<'line'> {
legend: {
display: false,
},
tooltip: {
enabled: false,
},
},
datasets: {
line: {
Expand Down
24 changes: 19 additions & 5 deletions webserver/web-interface/src/components/chart/ShotChart.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {
useEffect, useRef, useState, useMemo,
useEffect, useRef, useState, useMemo, useCallback,
} from 'react';
import PropTypes from 'prop-types';
import { Line } from 'react-chartjs-2';
Expand Down Expand Up @@ -152,10 +152,18 @@ function addDataPointToChartData(chartData, dataPoint, maxLength, chartRef) {
/* eslint-enable no-param-reassign */
}

function Chart({ data, newDataPoint, maxLength }) {
function Chart({
data, newDataPoint, maxLength, onHover,
}) {
const chartRef = useRef(null);
const theme = useTheme();
const config = useMemo(() => getShotChartConfig(theme), [theme]);
const [datapoints, setDatapoints] = useState([]);

const onHoverInternal = useCallback((hoverIndex) => {
onHover(datapoints[hoverIndex]);
}, [onHover, datapoints]);

const config = useMemo(() => getShotChartConfig(theme, onHoverInternal), [theme, onHoverInternal]);
const [chartData, setChartData] = useState(mapToChartData([], theme));

if (newDataPoint && data) {
Expand All @@ -167,8 +175,9 @@ function Chart({ data, newDataPoint, maxLength }) {
if (data === undefined || data === null) {
return;
}
setDatapoints(data);
setChartData(mapToChartData(data, theme));
}, [data, theme]);
}, [data, theme, setDatapoints, setChartData]);

// Adds newDataPoint to the end of the chart unless it detects that a new shot was started. More efficient.
useEffect(() => {
Expand All @@ -177,10 +186,13 @@ function Chart({ data, newDataPoint, maxLength }) {
}
if (newShotStarted(newDataPoint, chartData)) {
setChartData(mapToChartData([newDataPoint], theme));
setDatapoints([newDataPoint]);
} else {
addDataPointToChartData(chartData, newDataPoint, maxLength, chartRef);
setDatapoints([...datapoints, newDataPoint]);
}
}, [newDataPoint, theme, chartData, maxLength]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [newDataPoint, theme, maxLength, setDatapoints, setChartData]);

return (
<Line
Expand Down Expand Up @@ -209,10 +221,12 @@ Chart.propTypes = {
data: PropTypes.arrayOf(ShotChartDataPointType),
newDataPoint: ShotChartDataPointType,
maxLength: PropTypes.number,
onHover: PropTypes.func,
};

Chart.defaultProps = {
data: undefined,
newDataPoint: undefined,
maxLength: undefined,
onHover: undefined,
};
2 changes: 1 addition & 1 deletion webserver/web-interface/src/components/chart/StatBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function StatBox({
...sx,
}}
>
<Stack direction="row" alignContent="stretch">
<Stack direction="row" alignItems="center" sx={{ height: '100%' }}>
{icon && (
<Box display="flex" alignItems="center" color={color}>
<Typography fontSize="6px">{icon}</Typography>
Expand Down
121 changes: 71 additions & 50 deletions webserver/web-interface/src/pages/home/ShotDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import {
AppBar, Box, Dialog, IconButton, Toolbar, useTheme,
AppBar, Box, Dialog, IconButton, SxProps, Theme, Toolbar, useTheme,

} from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
Expand All @@ -10,7 +10,7 @@ import {
PressureStatBox, PumpFlowStatBox, TemperatureStatBox, TimeStatBox, WeightFlowStatBox, WeightStatBox,
} from '../../components/chart/StatBox';
import useShotDataStore from '../../state/ShotDataStore';
import { Shot } from '../../models/models';
import { Shot, ShotSnapshot } from '../../models/models';

interface ShotDialogProps {
open?: boolean;
Expand All @@ -21,8 +21,19 @@ interface ShotDialogProps {
export default function ShotDialog({ open, setOpen, historyShot }: ShotDialogProps) {
const { latestShotDatapoint } = useShotDataStore();
const theme = useTheme();
const fullHeightGraph = `calc(100vh - 64px - ${theme.spacing(2)})`;
const lessHeightGraph = `calc(75vh - 64px - ${theme.spacing(2)})`;
const fullHeightGraph = `calc(100vh - 64px - ${theme.spacing(1)})`;
const lessHeightGraph = `calc(75vh - 64px - ${theme.spacing(1)})`;
const [activeDatapoint, setActiveDatapoint] = useState<ShotSnapshot | undefined>(undefined);

useEffect(() => {
if (!historyShot || historyShot.datapoints.length === 0) return;
setActiveDatapoint(historyShot.datapoints[historyShot.datapoints.length - 1]);
}, [historyShot, setActiveDatapoint]);

useEffect(() => {
if (historyShot) return;
setActiveDatapoint(latestShotDatapoint);
}, [latestShotDatapoint, historyShot, setActiveDatapoint]);

return (
<Dialog
Expand All @@ -44,56 +55,15 @@ export default function ShotDialog({ open, setOpen, historyShot }: ShotDialogPro
{historyShot ? 'Reviewing shot from history' : 'Shot in progress'}
</Toolbar>
</AppBar>
<Grid container spacing={1} sx={{ mx: 0, mt: theme.spacing(2) }}>
<Grid container spacing={1} sx={{ mx: 0, mt: theme.spacing(1) }}>
<Grid xs={12} sm={9} sx={{ height: { xs: lessHeightGraph, sm: fullHeightGraph } }}>
<Box sx={{ position: 'relative', width: '100%', height: '100%' }}>
{historyShot && <ShotChart data={historyShot.datapoints} />}
{!historyShot && <ShotChart newDataPoint={latestShotDatapoint} />}
{historyShot && <ShotChart data={historyShot.datapoints} onHover={setActiveDatapoint} />}
{!historyShot && <ShotChart newDataPoint={latestShotDatapoint} onHover={setActiveDatapoint} />}
</Box>
</Grid>
<Grid xs={12} sm={3} sx={{ height: { xs: '25vh', sm: fullHeightGraph } }}>
<Grid container columns={3} spacing={1} sx={{ height: '100%', overflow: 'scroll' }}>
<Grid xs={1} sm={3}>
<TimeStatBox
timeInShot={latestShotDatapoint?.timeInShot || 0}
sx={{ height: '100%' }}
/>
</Grid>
<Grid xs={1} sm={3}>
<WeightStatBox
shotWeight={latestShotDatapoint?.shotWeight || 0}
sx={{ height: '100%' }}
/>
</Grid>
<Grid xs={1} sm={3}>
<PressureStatBox
pressure={latestShotDatapoint?.pressure || 0}
target={latestShotDatapoint?.targetPressure || 0}
sx={{ height: '100%' }}
/>
</Grid>
<Grid xs={1} sm={3}>
<PumpFlowStatBox
pumpFlow={latestShotDatapoint?.pumpFlow || 0}
target={latestShotDatapoint?.targetPumpFlow || 0}
sx={{ height: '100%' }}
/>
</Grid>
<Grid xs={1} sm={3}>
<WeightFlowStatBox
flow={latestShotDatapoint?.weightFlow || 0}
target={latestShotDatapoint?.targetPumpFlow || 0}
sx={{ height: '100%' }}
/>
</Grid>
<Grid xs={1} sm={3}>
<TemperatureStatBox
temperature={latestShotDatapoint?.temperature || 0}
target={latestShotDatapoint?.targetTemperature || 0}
sx={{ height: '100%' }}
/>
</Grid>
</Grid>
<StatBoxes activeDatapoint={activeDatapoint} />
</Grid>
</Grid>
</Dialog>
Expand All @@ -104,3 +74,54 @@ ShotDialog.defaultProps = {
open: false,
historyShot: undefined,
};

function StatBoxes({ activeDatapoint }: { activeDatapoint?: ShotSnapshot}) {
const boxSx:SxProps<Theme> = { height: { xs: '100%', sm: 'auto' } };

return (
<Grid container spacing={1} sx={{ height: { xs: '100%', sm: 'auto' }, overflow: 'scroll' }}>
<Grid xs={4} sm={12}>
<TimeStatBox
timeInShot={activeDatapoint?.timeInShot || 0}
sx={boxSx}
/>
</Grid>
<Grid xs={4} sm={12}>
<WeightStatBox
shotWeight={activeDatapoint?.shotWeight || 0}
sx={boxSx}
/>
</Grid>
<Grid xs={4} sm={12}>
<PressureStatBox
pressure={activeDatapoint?.pressure || 0}
target={activeDatapoint?.targetPressure || 0}
sx={boxSx}
/>
</Grid>
<Grid xs={4} sm={12}>
<PumpFlowStatBox
pumpFlow={activeDatapoint?.pumpFlow || 0}
target={activeDatapoint?.targetPumpFlow || 0}
sx={boxSx}
/>
</Grid>
<Grid xs={4} sm={12}>
<WeightFlowStatBox
flow={activeDatapoint?.weightFlow || 0}
target={activeDatapoint?.targetPumpFlow || 0}
sx={boxSx}
/>
</Grid>
<Grid xs={4} sm={12}>
<TemperatureStatBox
temperature={activeDatapoint?.temperature || 0}
target={activeDatapoint?.targetTemperature || 0}
sx={boxSx}
/>
</Grid>
</Grid>
);
}

StatBoxes.defaultProps = { activeDatapoint: undefined };

0 comments on commit ea7078c

Please sign in to comment.