Abhinandan-Kushwaha / react-native-gifted-charts

The most complete library for Bar, Line, Area, Pie, Donut, Stacked Bar and Population Pyramid charts in React Native. Allows 2D, 3D, gradient, animations and live data updates.
https://www.npmjs.com/package/react-native-gifted-charts
MIT License
772 stars 153 forks source link

Feature Request #763

Closed Hadionly closed 2 months ago

Hadionly commented 2 months ago

I am suggesting an improved label component for use with pie charts. The most effective method I’ve encountered for fitting numerous slices involves placing labels offset by a certain distance from the pie chart (aligned to the right). The Y position of each label is near the middle of the slice and adjusted to avoid overlapping with other labels, using a long stem to clearly indicate the corresponding slice. For example:

image

Hadionly commented 2 months ago

Is there a workaround for now as I need something like that please?

Abhinandan-Kushwaha commented 2 months ago

Hi @Hadionly 👋 Thanks for requesting this feature. If you want, you can add this feature in this repo and raise a PR. Otherwise I will try to add this feature soon.

Hadionly commented 2 months ago

Hey @Abhinandan-Kushwaha 🙌 I really love your library by the way the best out there! I'll wait for you for next version for now as idk how to do it actually (if you have any guidance on where I should check I'll try it out as i've never contributed to a public repo before) but thanks a bunch for the hard work

Abhinandan-Kushwaha commented 2 months ago

Hi @Hadionly 👋

You can now add external labels in Pie and donut charts using the below props-

  1. showExternalLabels (boolean)
  2. labelLineConfig (object of LabelLineConfig type)
  3. externalLabelComponent (svg component for label, a callback function with 2 parameters- item and index) The labelLineConfig prop is an object of LabelLineConfig type described below-
type LabelLineConfig = {
  length?: number; // default 10
  tailLength?: number; // default 8
  color?: ColorValue; // default 'black'
  thickness?: number; // default 1
  labelComponentWidth?: number; // default 20
  labelComponentHeight?: number; // default 10
  labelComponentMargin?: number; // default 4
};

Here's a simple example-

const SimplePie = () => {
  const pieData = [
    {value: 54, color: '#177AD5', text: '54k'},
    {value: 54, color: '#79D2DE', text: '30k'},
    {value: 26, color: '#ED6665', text: '26k'},
  ];
  return (
    <PieChart
      data={pieData}
      showExternalLabels
      externalLabelComponent={(item, index) => <Text>{item?.text}</Text>}
    />
  );
};

The output of the above code is-

Screenshot 2024-08-06 at 2 43 15 AM

This feature is available from versions 1.4.28 onwards. Please use the latest version of the library.

Hadionly commented 2 months ago

Hey Bro @Abhinandan-Kushwaha I'm really impressed by how fast your development is, Great job 🙌👌 I tried it and everything is working fine but I'm just stuck with following issue: image as you can see there is something like an accumulation of the labels on top.

code snippet:

  const pieData = [
    { value: 54, color: "#177AD5", text: "54k" },
    { value: 54, color: "#79D2DE", text: "30k" },
    { value: 26, color: "#ED6665", text: "26k" },
  ];
   return (
    <>
      <Text
        style={{ color: COLORS.white, alignSelf: "center", marginTop: s(10) }}>
        {chartLabel}
      </Text>
      {/* Pie chart or Bar Chart*/}
      <View
        style={{
          alignItems: "center",
          justifyContent: "center",
          marginBottom: s(20),
          // paddingTop: s(20),
          overflow: "hidden",
          flex: 1,
        }}>
        {pie ? (
          <PieChart
            data={pieData}
            showExternalLabels
            externalLabelComponent={(item) => (
              <Text style={{ color: COLORS.white, zIndex: 10 }}>
                {item?.text}
              </Text>
            )}
            labelLineConfig={{
              color: COLORS.white,
              labelComponentMargin: -15,
              length: 20,
            }}
            // innerCircleColor={COLORS.bgSecondary}
            // innerCircleBorderWidth={4}
            // innerCircleBorderColor={"white"}
            // strokeColor="white"
            // strokeWidth={4}
            // donut
            // textSize={18}
            // showTextBackground={true}
            // centerLabelComponent={() => {
            //   return (
            //     <View
            //       style={{ alignItems: "center", justifyContent: "center" }}>
            //       <Text style={{ color: "white", fontSize: 22 }}>
            //         {data.length}
            //       </Text>
            //       <Text style={{ color: "white", fontSize: 12 }}>
            //         {middleLabel}
            //       </Text>
            //     </View>
            //   );
            // }}
          />
Abhinandan-Kushwaha commented 2 months ago

@Hadionly This is happening because inside externalLabelComponent you are using <Text> imported from 'react-native'.

externalLabelComponent is supposed to render svg, so you can-

import {Text as SvgText} from 'react-native-svg';

And then use <SvgText> instead of <Text>

externalLabelComponent={(item) => (
  <SvgText style={{ color: COLORS.white, zIndex: 10 }}>
    {item?.text}
  </SvgText>
)}
Hadionly commented 2 months ago

Hey bro @Abhinandan-Kushwaha Thank you so much, now it's working great 👌🙌. I just would like to know one last thing: image as you can see the labels are being cut off, is there a way to increase it's zIndex, I tried but seems like it's not working. at first I thought it was because of the overflow hidden but after I comment it nothing actually happens

code snipped:

      <View
        style={{
          alignItems: "center",
          justifyContent: "center",
          marginBottom: s(20),
          // paddingTop: s(20),
          overflow: "hidden",
          flex: 1,
        }}>
          <PieChart
            data={data}
            showExternalLabels
            externalLabelComponent={(item) => (
              <SvgText fontSize={20} fill={COLORS.white} fontWeight={"400"}>
                {item?.value.toFixed(0)}%
              </SvgText>
            )}
            labelLineConfig={{
              color: COLORS.white,
              length: 4,
            }}
          />
Abhinandan-Kushwaha commented 2 months ago

@Hadionly You can use the extraRadius prop and pass a value bigger than 40. The default value of extraRadius on using showExternalLabels is 40, but if the labels are getting cropped, you should pass a value bigger than 40.

See the note in props list here

Hadionly commented 2 months ago

Thank you so much you are just amazing brother @Abhinandan-Kushwaha 🙌😍