UNPKG

sfm-uikit-react-native

Version:

It is a react native component for SmartFloMeet users.

141 lines (123 loc) 4.46 kB
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;