feat: separate y-axis scales per metric with three stacked charts
This commit is contained in:
parent
1275a229f2
commit
3e4e7d6266
1 changed files with 72 additions and 54 deletions
|
|
@ -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' },
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue