feat: separate y-axis scales per metric with three stacked charts

This commit is contained in:
Eddy de Vink 2026-06-27 23:51:26 +02:00
commit 3e4e7d6266

View file

@ -11,6 +11,52 @@ const COLORS = {
muscleMass: '#2196F3', muscleMass: '#2196F3',
} }
function SingleChart({
title,
data,
labels,
color,
unit,
}: {
title: string
data: number[]
labels: string[]
color: string
unit: string
}) {
return (
<View style={styles.chartBlock}>
<Text style={styles.chartTitle}>{title}</Text>
<LineChart
data={{ labels, datasets: [{ data, color: () => color, strokeWidth: 2 }] }}
width={Dimensions.get('window').width - 32}
height={180}
yAxisSuffix={unit}
chartConfig={{
backgroundColor: '#ffffff',
backgroundGradientFrom: '#ffffff',
backgroundGradientTo: '#ffffff',
decimalPlaces: 1,
color: (opacity = 1) => `rgba(${hexToRgb(color)}, ${opacity})`,
labelColor: () => '#666',
propsForDots: { r: '3' },
}}
bezier
style={styles.chart}
withInnerLines={false}
fromZero={false}
/>
</View>
)
}
function hexToRgb(hex: string): string {
const r = parseInt(hex.slice(1, 3), 16)
const g = parseInt(hex.slice(3, 5), 16)
const b = parseInt(hex.slice(5, 7), 16)
return `${r}, ${g}, ${b}`
}
export default function WeightChart({ measurements }: Props) { export default function WeightChart({ measurements }: Props) {
if (measurements.length < 2) { if (measurements.length < 2) {
return null return null
@ -35,57 +81,31 @@ export default function WeightChart({ measurements }: Props) {
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.title}>Gewicht & Lichaamssamenstelling</Text> <Text style={styles.title}>Voortgang (30 dagen)</Text>
<LineChart
data={{ <SingleChart
labels: sparseLabels, title="Gewicht"
datasets: [ data={dataPoints.map(m => m.weight)}
{ labels={sparseLabels}
data: dataPoints.map(m => m.weight), color={COLORS.weight}
color: () => COLORS.weight, unit=" kg"
strokeWidth: 2, />
},
{ <SingleChart
data: dataPoints.map(m => m.fatMass), title="Vetmassa"
color: () => COLORS.fatMass, data={dataPoints.map(m => m.fatMass)}
strokeWidth: 2, labels={sparseLabels}
}, color={COLORS.fatMass}
{ unit=" kg"
data: dataPoints.map(m => m.muscleMass), />
color: () => COLORS.muscleMass,
strokeWidth: 2, <SingleChart
}, title="Spiermassa"
], data={dataPoints.map(m => m.muscleMass)}
}} labels={sparseLabels}
width={Dimensions.get('window').width - 32} color={COLORS.muscleMass}
height={240} unit=" kg"
yAxisSuffix=" kg"
chartConfig={{
backgroundColor: '#ffffff',
backgroundGradientFrom: '#ffffff',
backgroundGradientTo: '#ffffff',
decimalPlaces: 1,
color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
labelColor: () => '#666',
propsForDots: { r: '3' },
}}
bezier
style={styles.chart}
/> />
<View style={styles.legend}>
<View style={styles.legendItem}>
<View style={[styles.legendDot, { backgroundColor: COLORS.weight }]} />
<Text style={styles.legendLabel}>Gewicht</Text>
</View>
<View style={styles.legendItem}>
<View style={[styles.legendDot, { backgroundColor: COLORS.fatMass }]} />
<Text style={styles.legendLabel}>Vetmassa</Text>
</View>
<View style={styles.legendItem}>
<View style={[styles.legendDot, { backgroundColor: COLORS.muscleMass }]} />
<Text style={styles.legendLabel}>Spiermassa</Text>
</View>
</View>
</View> </View>
) )
} }
@ -93,9 +113,7 @@ export default function WeightChart({ measurements }: Props) {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { marginHorizontal: 16, marginBottom: 16 }, container: { marginHorizontal: 16, marginBottom: 16 },
title: { fontSize: 18, fontWeight: '700', marginBottom: 8 }, title: { fontSize: 18, fontWeight: '700', marginBottom: 8 },
chartBlock: { marginBottom: 12 },
chartTitle: { fontSize: 13, fontWeight: '600', color: '#555', marginBottom: 4, marginLeft: 4 },
chart: { borderRadius: 12 }, chart: { borderRadius: 12 },
legend: { flexDirection: 'row', justifyContent: 'center', gap: 16, marginTop: 8 },
legendItem: { flexDirection: 'row', alignItems: 'center', gap: 4 },
legendDot: { width: 10, height: 10, borderRadius: 5 },
legendLabel: { fontSize: 12, color: '#666' },
}) })