diff --git a/samples/App.tsx b/samples/App.tsx index 841a267..5fc5e1d 100644 --- a/samples/App.tsx +++ b/samples/App.tsx @@ -1,3 +1,5 @@ +// App.tsx + import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import NavBar from './utils/NavBar'; @@ -8,6 +10,9 @@ import DoubleLayerPieChart from './pages/DoubleLayerPieChart'; import PieChart from './pages/PieChart'; import StraightAnglePieChart from './pages/StraightAnglePieChart'; import PieChartWithPaddingAngle from './pages/PieChartWithPaddingAngle'; +import PieChartWithCustomizedLabel from './pages/PieChartWithCustomizedLabel'; +import CustomActiveShapePieChart from './pages/CustomActiveShapePieChart'; + const App = () => { return ( @@ -21,7 +26,9 @@ const App = () => { } /> } /> } /> - } /> + } /> + } /> + } /> diff --git a/samples/pages/CustomActiveShapePieChart.tsx b/samples/pages/CustomActiveShapePieChart.tsx new file mode 100644 index 0000000..a70dc41 --- /dev/null +++ b/samples/pages/CustomActiveShapePieChart.tsx @@ -0,0 +1,67 @@ +import React, { useState } from 'react'; +import PieChart from '../../src/PieChart'; +import Pie from '../../src/Pie'; +import PolarGrid from '../../src/PolarGrid'; +import Tooltip from '../../src/Tooltip'; +import Legend from '../../src/Legend'; +import PieChartControls from '../utils/PieChartControls'; + +const data01 = [ + { name: 'Group A', value: 400 }, + { name: 'Group B', value: 300 }, + { name: 'Group C', value: 300 }, + { name: 'Group D', value: 200 }, + { name: 'Group E', value: 278 }, + { name: 'Group F', value: 189 }, +]; + +const CustomActiveShapePieChart: React.FC = () => { + const [pies, setPies] = useState([ + { + id: 1, + innerRadius: 0, + outerRadius: 80, + cx: '50%', + cy: '50%', + showLabels: true, + startAngle: 0, + endAngle: 360, + label: 'percent', + activeShape: true, + }, + ]); + const [showPolarGrid, setShowPolarGrid] = useState(true); + + return ( +
+

Custom Active Shape Pie Chart

+
+ + + {showPolarGrid && } + {pies.map((pie) => ( + + ))} + + + +
+
+ ); +}; + +export default CustomActiveShapePieChart; diff --git a/samples/pages/PieChartWithCustomizedLabel.tsx b/samples/pages/PieChartWithCustomizedLabel.tsx new file mode 100644 index 0000000..fe0cc53 --- /dev/null +++ b/samples/pages/PieChartWithCustomizedLabel.tsx @@ -0,0 +1,70 @@ +import React, { useState } from 'react'; +import PieChart from '../../src/PieChart'; +import Pie from '../../src/Pie'; +import PolarGrid from '../../src/PolarGrid'; +import Tooltip from '../../src/Tooltip'; +import Legend from '../../src/Legend'; +import PieChartControls from '../utils/PieChartControls'; + +const data01 = [ + { "name": "Group A", "value": 400 }, + { "name": "Group B", "value": 300 }, + { "name": "Group C", "value": 300 }, + { "name": "Group D", "value": 200 }, + { "name": "Group E", "value": 278 }, + { "name": "Group F", "value": 189 } +]; + +const PieChartWithCustomizedLabel: React.FC = () => { + const [pies, setPies] = useState([ + { + id: 1, + innerRadius: 0, + outerRadius: 80, + cx: '50%', + cy: '50%', + showLabels: true, + startAngle: 0, + endAngle: 360, + label: ['A', 'B', 'C', 'D', 'E', 'F'], + }, + ]); + const [showPolarGrid, setShowPolarGrid] = useState(true); + + return ( +
+

PieChart With Customized Label

+
+ + + {showPolarGrid && } + {pies.map((pie) => ( + + ))} + + + +
+
+ ); +}; + +export default PieChartWithCustomizedLabel; diff --git a/samples/utils/NavBar.tsx b/samples/utils/NavBar.tsx index a3a3c0b..d300817 100644 --- a/samples/utils/NavBar.tsx +++ b/samples/utils/NavBar.tsx @@ -1,3 +1,4 @@ +// NavBar.tsx import React, { useState } from 'react'; import { Link, useLocation } from 'react-router-dom'; @@ -21,10 +22,12 @@ const NavBar = () => { { category: 'Pie Charts', items: [ - { name: 'Double Layer Pie Chart', path: '/double-layer-pie' }, { name: 'Pie Chart', path: '/pie-chart' }, + { name: 'Double Layer Pie Chart', path: '/double-layer-pie' }, { name: 'Straight Angle Pie Chart', path: '/straight-angle-pie' }, - { name: 'Pie Chart with Padding Angle', path: '/padding-angle-pie' }, + { name: 'Pie Chart with Padding Angle', path: '/pie-chart-with-padding-angle' }, + { name: 'Pie Chart with Customized Label', path: '/pie-chart-with-customized-label' }, + { name: 'Custom Active Shape Pie Chart', path: '/custom-active-shape-pie' }, ], }, ]; diff --git a/samples/utils/PieChartControls.tsx b/samples/utils/PieChartControls.tsx index bbc8216..cc5f0b7 100644 --- a/samples/utils/PieChartControls.tsx +++ b/samples/utils/PieChartControls.tsx @@ -3,28 +3,32 @@ import React from 'react'; interface PieChartControlsProps { pies: Array<{ id: number; - innerRadius: number; - outerRadius: number; - cx: string | number; - cy: string | number; - showLabels: boolean; - startAngle: number; - endAngle: number; - paddingAngle: number; + innerRadius?: number; + outerRadius?: number; + cx?: string | number; + cy?: string | number; + showLabels?: boolean; + startAngle?: number; + endAngle?: number; + label?: 'percent' | string[] | boolean; + paddingAngle?: number; + activeShape?: boolean; }>; setPies: React.Dispatch>>; - showPolarGrid: boolean; - setShowPolarGrid: (show: boolean) => void; + showPolarGrid?: boolean; + setShowPolarGrid?: (show: boolean) => void; } const PieChartControls: React.FC = ({ pies, setPies, showPolarGrid, setShowPolarGrid }) => { @@ -34,97 +38,149 @@ const PieChartControls: React.FC = ({ pies, setPies, show setPies(updatedPies); }; + const handleLabelChange = (index: number, value: string) => { + const updatedPies = [...pies]; + if (value === 'percent') { + updatedPies[index].label = 'percent'; + } else if (value) { + updatedPies[index].label = value.split(','); + } else { + updatedPies[index].label = true; + } + setPies(updatedPies); + }; + return (

Chart Settings

-
- -
+ {setShowPolarGrid && ( +
+ +
+ )} {pies.map((pie, index) => (

Pie {index + 1} Settings

-
- - handlePieChange(index, 'innerRadius', parseInt(e.target.value, 10))} - className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" - /> -
-
- - handlePieChange(index, 'outerRadius', parseInt(e.target.value, 10))} - className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" - /> -
-
- - handlePieChange(index, 'startAngle', parseInt(e.target.value, 10))} - className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" - /> -
-
- - handlePieChange(index, 'endAngle', parseInt(e.target.value, 10))} - className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" - /> -
-
- - handlePieChange(index, 'paddingAngle', parseInt(e.target.value, 10))} - className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" - /> -
-
- - handlePieChange(index, 'cx', e.target.value)} - className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" - /> -
-
- - handlePieChange(index, 'cy', e.target.value)} - className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" - /> -
- + {pie.innerRadius !== undefined && ( +
+ + handlePieChange(index, 'innerRadius', parseInt(e.target.value, 10))} + className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" + /> +
+ )} + {pie.outerRadius !== undefined && ( +
+ + handlePieChange(index, 'outerRadius', parseInt(e.target.value, 10))} + className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" + /> +
+ )} + {pie.startAngle !== undefined && ( +
+ + handlePieChange(index, 'startAngle', parseInt(e.target.value, 10))} + className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" + /> +
+ )} + {pie.endAngle !== undefined && ( +
+ + handlePieChange(index, 'endAngle', parseInt(e.target.value, 10))} + className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" + /> +
+ )} + {pie.cx !== undefined && ( +
+ + handlePieChange(index, 'cx', e.target.value)} + className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" + /> +
+ )} + {pie.cy !== undefined && ( +
+ + handlePieChange(index, 'cy', e.target.value)} + className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" + /> +
+ )} + {pie.paddingAngle !== undefined && ( +
+ + handlePieChange(index, 'paddingAngle', parseInt(e.target.value, 10))} + className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" + /> +
+ )} + {pie.showLabels !== undefined && ( + + )} + {pie.label !== undefined && ( +
+ + handleLabelChange(index, e.target.value)} + className="w-full px-3 py-2 border border-purple-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 transition duration-300 ease-in-out" + /> +
+ )} + {pie.activeShape !== undefined && ( + + )}
))} diff --git a/src/Pie/index.tsx b/src/Pie/index.tsx index 11e587b..4088283 100644 --- a/src/Pie/index.tsx +++ b/src/Pie/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; interface PieProps { @@ -10,10 +10,11 @@ interface PieProps { innerRadius?: number; outerRadius: number; fill: string; - label?: boolean; + label?: 'percent' | string[]; startAngle?: number; endAngle?: number; paddingAngle?: number; + activeShape?: boolean; } const Pie: React.FC = ({ @@ -25,10 +26,11 @@ const Pie: React.FC = ({ innerRadius = 0, outerRadius, fill, - label = false, + label = [], startAngle = 0, endAngle = 360, paddingAngle = 0, + activeShape = false, }) => { const computedCx = typeof cx === 'string' && cx.endsWith('%') ? (parseFloat(cx) / 100) * 730 : cx; const computedCy = typeof cy === 'string' && cy.endsWith('%') ? (parseFloat(cy) / 100) * 250 : cy; @@ -38,6 +40,8 @@ const Pie: React.FC = ({ let currentAngle = startAngle + 180; + const [activeIndex, setActiveIndex] = useState(null); + return ( {data.map((entry, index) => { @@ -62,19 +66,41 @@ const Pie: React.FC = ({ currentAngle = nextAngle + paddingAngle; + let labelText = ''; + if (label === 'percent') { + labelText = `${((value / totalValue) * 100).toFixed(1)}%`; + } else if (Array.isArray(label) && label[index]) { + labelText = label[index]; + } else { + labelText = `${value}`; + } + + const isActive = activeShape && activeIndex === index; + const adjustedOuterRadius = isActive ? outerRadius + 10 : outerRadius; + return ( - - + setActiveIndex(index)} + onMouseLeave={() => setActiveIndex(null)} + > + {label && ( - {entry[dataKey]} + {labelText} )}