Use new chart library in ViewGraph

This commit is contained in:
Brandon Presley 2023-10-26 12:20:15 +13:00
parent f28406b4c4
commit 1c10e0f632
2 changed files with 78 additions and 93 deletions

108
Chart.tsx
View File

@ -1,69 +1,55 @@
import { useTheme } from "@react-navigation/native"; import { useMemo } from "react";
import * as shape from "d3-shape"; import { useWindowDimensions } from "react-native";
import { View } from "react-native"; import { LineChart } from "react-native-chart-kit";
import { Grid, LineChart, XAxis, YAxis } from "react-native-svg-charts"; import { AbstractChartConfig } from "react-native-chart-kit/dist/AbstractChart";
import { CombinedDarkTheme, CombinedDefaultTheme } from "./App"; import { PADDING } from "./constants";
import { MARGIN, PADDING } from "./constants";
import useDark from "./use-dark"; import useDark from "./use-dark";
import { useTheme } from "react-native-paper";
export default function Chart({ interface ChartProps {
yData, labels: string[];
xFormat, data: number[];
xData, preserve?: number;
yFormat, }
}: {
yData: number[]; export default function Chart({ labels, data, preserve = 3 }: ChartProps) {
xData: unknown[]; const { width } = useWindowDimensions();
xFormat: (value: any, index: number) => string;
yFormat: (value: any) => string;
}) {
const { colors } = useTheme(); const { colors } = useTheme();
const dark = useDark();
const axesSvg = { const config: AbstractChartConfig = {
fontSize: 10, backgroundGradientFrom: colors.background,
fill: dark backgroundGradientTo: colors.elevation.level1,
? CombinedDarkTheme.colors.text color: () => colors.primary,
: CombinedDefaultTheme.colors.text,
}; };
const verticalContentInset = { top: 10, bottom: 10 };
const xAxisHeight = 30; const pruned = useMemo(() => {
const newPruned = [...labels];
if (labels.length <= preserve + 2) return labels;
let interval = Math.floor((labels.length - 2) / (preserve + 1));
for (let i = 1; i < labels.length - 1; i++) {
if ((i - 1) % interval !== 0 || i === 1) {
newPruned[i] = "";
}
}
return newPruned;
}, [labels, preserve]);
console.log({ labels, data, pruned, preserve });
return ( return (
<> <LineChart
<View height={400}
style={{ width={width - 20}
height: 300, data={{
padding: PADDING, labels: pruned,
flexDirection: "row", datasets: [
}} {
> data,
<YAxis },
data={yData} ],
style={{ marginBottom: xAxisHeight }} }}
contentInset={verticalContentInset} chartConfig={config}
svg={axesSvg} />
formatLabel={yFormat}
/>
<View style={{ flex: 1, marginLeft: MARGIN }}>
<LineChart
style={{ flex: 1 }}
data={yData}
contentInset={verticalContentInset}
curve={shape.curveBasis}
svg={{
stroke: colors.primary,
}}
>
<Grid />
</LineChart>
<XAxis
data={xData}
formatLabel={xFormat}
contentInset={{ left: 15, right: 16 }}
svg={axesSvg}
/>
</View>
</View>
</>
); );
} }

View File

@ -1,5 +1,4 @@
import { RouteProp, useRoute } from "@react-navigation/native"; import { RouteProp, useRoute } from "@react-navigation/native";
import { format } from "date-fns";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { View } from "react-native"; import { View } from "react-native";
import { FileSystem } from "react-native-file-access"; import { FileSystem } from "react-native-file-access";
@ -16,6 +15,7 @@ import GymSet from "./gym-set";
import { Metrics } from "./metrics"; import { Metrics } from "./metrics";
import { Periods } from "./periods"; import { Periods } from "./periods";
import Volume from "./volume"; import Volume from "./volume";
import { format } from "date-fns";
export default function ViewGraph() { export default function ViewGraph() {
const { params } = useRoute<RouteProp<GraphsPageParams, "ViewGraph">>(); const { params } = useRoute<RouteProp<GraphsPageParams, "ViewGraph">>();
@ -70,41 +70,38 @@ export default function ViewGraph() {
}, [params.name, metric, period]); }, [params.name, metric, period]);
const charts = useMemo(() => { const charts = useMemo(() => {
if ( let periodFormat = "do";
(metric === Metrics.Volume && volumes?.length === 0) || if (period === Periods.Weekly) periodFormat = "iii";
(metric === Metrics.Best && weights?.length === 0) || else if (period === Periods.Yearly) periodFormat = "P";
(metric === Metrics.OneRepMax && weights?.length === 0) let preserve = 3;
) { if (period === Periods.Yearly) preserve = 1;
return <List.Item title="No data yet." />;
} if (metric === Metrics.Volume && Number(volumes?.length) > 0)
if (metric === Metrics.Volume && volumes?.length && weights?.length) {
return ( return (
<Chart <Chart
yData={volumes.map((v) => v.value)} data={volumes.map((volume) => volume.value)}
yFormat={(value: number) => labels={volumes.map((volume) =>
`${value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}${ format(new Date(volume.created), periodFormat)
volumes[0].unit || "kg" )}
}` preserve={preserve}
} />
xData={weights} );
xFormat={(_value, index) => if (
format(new Date(weights[index].created), "d/M") (metric === Metrics.Best || metric === Metrics.OneRepMax) &&
} Number(weights?.length) > 0
)
return (
<Chart
data={weights.map((set) => set.weight)}
labels={weights.map((set) =>
format(new Date(set.created), periodFormat)
)}
preserve={preserve}
/> />
); );
}
return ( return <List.Item title="No data yet." />;
<Chart }, [volumes, weights, metric, period]);
yData={weights?.map((set) => set.weight) || []}
yFormat={(value) => `${value}${weights?.[0].unit}`}
xData={weights || []}
xFormat={(_value, index) =>
format(new Date(weights?.[index].created), "d/M")
}
/>
);
}, [volumes, weights, metric]);
return ( return (
<> <>
@ -144,7 +141,9 @@ export default function ViewGraph() {
onChange={(value) => setPeriod(value as Periods)} onChange={(value) => setPeriod(value as Periods)}
value={period} value={period}
/> />
{charts} <View style={{ paddingTop: PADDING }}>
{(weights || volumes) && charts}
</View>
</View> </View>
</> </>
); );