sfm-uikit-react-native
Version:
It is a react native component for SmartFloMeet users.
141 lines (123 loc) • 4.46 kB
JavaScript
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import Svg, { Path, Text as SvgText } from 'react-native-svg';
// Function to calculate the path for each slice
const calculateArc = (cx, cy, radius, startAngle, endAngle) => {
const start = {
x: cx + radius * Math.cos((Math.PI / 180) * startAngle),
y: cy + radius * Math.sin((Math.PI / 180) * startAngle),
};
const end = {
x: cx + radius * Math.cos((Math.PI / 180) * endAngle),
y: cy + radius * Math.sin((Math.PI / 180) * endAngle),
};
const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
return `M ${cx} ${cy} L ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${end.x} ${end.y} Z`;
};
// Function to calculate the position for percentage text
const calculateTextPosition = (cx, cy, radius, startAngle, endAngle) => {
const angle = (startAngle + endAngle) / 2; // Middle of the arc
return {
x: cx + (radius / 2) * Math.cos((Math.PI / 180) * angle),
y: cy + (radius / 2) * Math.sin((Math.PI / 180) * angle),
};
};
const PieChart = ({ data = [], radius = 100, cx = 150, cy = 150 }) => {
const total = data.reduce((sum, item) => sum + item.percentage, 0);
let startAngle = 0;
// Handle the case where total is 100% and any slice has 100%
if (total === 100 && data.some(slice => slice.percentage === 100)) {
// Only draw one slice
const fullSlice = data.find(slice => slice.percentage === 100);
return (
<View style={{ alignItems: 'center', flex: 1 }}>
<Svg height={radius * 2 + 50} width={radius * 2 + 50}>
<Path
d={`M ${cx} ${cy - radius} A ${radius} ${radius} 0 1 1 ${cx} ${cy + radius} A ${radius} ${radius} 0 1 1 ${cx} ${cy - radius} Z`}
fill={fullSlice.color}
/>
{/* Centered Text for 100% Slice */}
<SvgText
x={cx}
y={cy}
fill="#fff"
fontSize="16"
fontWeight="bold"
textAnchor="middle"
>
{((fullSlice.percentage / total) * 100).toFixed(1)}%
</SvgText>
</Svg>
{/* Chips below the Pie Chart */}
<View style={styles.chipListContainer}>
{data.map((item, index) => (
<View key={index} style={[styles.chip, { backgroundColor: item.color }]}>
<Text style={styles.chipText}>{item.label}</Text>
</View>
))}
</View>
</View>
);
}
return (
<View style={{ alignItems: 'center', flex: 1 }}>
<Svg height={radius * 2 + 50} width={radius * 2 + 50}>
{data.map((slice, index) => {
// Skip slice if the percentage is 0
if (slice.percentage === 0) return null;
const angle = (slice.percentage / total) * 360;
const endAngle = startAngle + angle;
const path = calculateArc(cx, cy, radius, startAngle, endAngle);
// Calculate text position for percentage
const textPosition = calculateTextPosition(cx, cy, radius, startAngle, endAngle);
const percentage = ((slice.percentage / total) * 100).toFixed(1) + '%';
startAngle = endAngle;
return (
<React.Fragment key={index}>
<Path d={path} fill={slice.color} />
<SvgText
x={textPosition.x}
y={textPosition.y}
fill="#fff"
fontSize="12"
fontWeight="bold"
textAnchor="middle"
>
{percentage}
</SvgText>
</React.Fragment>
);
})}
</Svg>
{/* Chips below the Pie Chart */}
<View style={styles.chipListContainer}>
{data.map((item, index) => (
<View key={index} style={[styles.chip, { backgroundColor: item.color }]}>
<Text style={styles.chipText}>{item.label}</Text>
</View>
))}
</View>
</View>
);
};
const styles = StyleSheet.create({
chipListContainer: {
marginTop: 20,
flexDirection: 'row',
flexWrap: 'wrap', // Allow chips to wrap to the next line
justifyContent: 'center',
},
chip: {
paddingVertical: 5,
paddingHorizontal: 10,
borderRadius: 20,
margin: 5, // Margin between chips
alignItems: 'center',
},
chipText: {
color: '#fff',
fontSize: 14,
fontWeight: 'bold',
},
});
export default PieChart;