Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pie chart external label alignment left on left side of pie. #806

Closed
ford-thomas-w opened this issue Aug 30, 2024 · 10 comments
Closed

Pie chart external label alignment left on left side of pie. #806

ford-thomas-w opened this issue Aug 30, 2024 · 10 comments

Comments

@ford-thomas-w
Copy link

ford-thomas-w commented Aug 30, 2024

Screenshot 2024-08-29 at 7 57 22 PM

Can the labels on the left side be right aligned automatically, so they align with the lines on both sides? If not, perhaps add some item property to distinquish between the left and right side labels and example of how to use that via the externalLabelComponent attribute?

                <PieChart
                    data={data}
                    showExternalLabels
                    paddingHorizontal={10}
                    externalLabelComponent={(item) => (
                        <SvgText fontSize={14} fontWeight={"bold"} fontFamily='Arial'>
                            {item?.text}
                        </SvgText>
                    )}
                    extraRadius={150}
                    labelLineConfig={{
                        labelComponentWidth: 150,
                        length: 10,
                    }}
                    donut
                    radius={90}
                    innerRadius={60}
                />
@Abhinandan-Kushwaha
Copy link
Owner

Hi @ford-thomas-w I have a workaround for this. You can increase the length property inside labelLineConfig from 10 to some bigger value like 160. This will make the line longer and it will start overlapping with the label text. To avoid the overlapping, we can give a backgroundColor to the label texts that matches with the rest of the backgound (white in your case).

Since Svg text does NOT have the backgroundColor property, we can draw a rectangle beneath the text and fill it with the backgroundColor.

You can replace your externalLabelComponent with the below code-

externalLabelComponent={(item, index) => (
  <G>
    <Rect
      x={0}
      y={-15}
      width={(item?.text?.length ?? 0) * 6.8}
      height={20}
      fill={'white'}
    />
    <SvgText fontSize={14} fontWeight={'bold'} fontFamily="Arial">
      {item?.text}
    </SvgText>
  </G>
)}

@ford-thomas-w
Copy link
Author

ford-thomas-w commented Sep 1, 2024

Hi @Abhinandan-Kushwaha , thank you for your help! Very creative solution! But took a little more tweaking to get it looking right on both sides. So, I wanted to share a complete example in case you would like to add some documentation about this to help others as well.

import { View } from 'react-native';
import React from 'react'
import { PieChart } from "react-native-gifted-charts";
import { G, Rect, Text as SvgText } from 'react-native-svg';

export type PieData = {
    value: number;
    text: string;
    labelLeft?: boolean;
}

export default function App() {
    let data: PieData[] = [
        {
            "value": 1,
            "text": "AP News",
        },
        {
            "value": 1,
            "text": "BBC News",
        },
        {
            "value": 1,
            "text": "CBS News",
        },
        {
            "value": 1,
            "text": "Forbes",
        },
        {
            "value": 1,
            "text": "NBC News",
        },
        {
            "value": 4,
            "text": "The New York Times",
        },
        {
            "value": 1,
            "text": "Reuters",
        },
        {
            "value": 1,
            "text": "The Guardian",
        },
        {
            "value": 1,
            "text": "The Washington Post",
        }
    ];

    const calcLabelPosition = (data: PieData[]) => {
        const totalValue = data.reduce((acc, item) => acc + item.value, 0);
        let accumulatedPercentage = 0;
        return data.map(item => {
            const percentage = ((item.value / totalValue) * 100);
            accumulatedPercentage += percentage;
            return {
                ...item,
                labelLeft: accumulatedPercentage > 50,
            };
        });
    };

    data = calcLabelPosition(data);
    return (
        <View>
            <PieChart
                data={data}
                showExternalLabels
                paddingHorizontal={10}
                externalLabelComponent={(item) => {
                    const itm = item as PieData;
                    const shiftX = itm.labelLeft ? 90 : -40;
                    return <G>
                        <Rect
                            x={itm.labelLeft ? shiftX : shiftX - 5}
                            y={-15}
                            width={(item?.text?.length ?? 0) * 8}
                            height={20}
                            fill={'white'}
                        />
                        <SvgText x={shiftX} fontSize={14} fontWeight={'bold'} fontFamily="Arial">
                            {item?.text}
                        </SvgText>
                    </G>
                }}
                extraRadius={150}
                labelLineConfig={{
                    labelComponentWidth: 150,
                    length: 60,
                }}
                donut
                radius={90}
                innerRadius={60}
            />
        </View>
    );
}

@ford-thomas-w
Copy link
Author

ford-thomas-w commented Sep 1, 2024

Screenshot 2024-08-31 at 9 25 52 PM The code above is a simplified version of this.

@Abhinandan-Kushwaha
Copy link
Owner

Thanks for sharing @ford-thomas-w

@ford-thomas-w
Copy link
Author

Screenshot 2024-09-02 at 6 43 52 PM

@Abhinandan-Kushwaha I'm afraid that was premature. When the dataset changes a bit, labels overlap as shown in the image above. I'm aware of your recent change for that in 1.4.31 which is the version used in that screenshot. I also tried 'avoidOverlappingOfLabels' but that did nothing I could see. I see you've also closed that ticket: #801
I don't know if the problem in this case is related to the workaround for this scenario, i.e. with the svg backgrounds. Would it be possible to re-open that or this one?

@Abhinandan-Kushwaha
Copy link
Owner

@ford-thomas-w Can you share the new dataSet and code?

@ford-thomas-w
Copy link
Author

@Abhinandan-Kushwaha Much appreciated! Here's a repo with a simple example you can run that shows a few different data sets.
https://github.com/symthink/shared-components
Or, if you just want to look at the code and data it's in the "components" and "data" folder.

@Abhinandan-Kushwaha
Copy link
Owner

Hi @ford-thomas-w I had ensured that consecutive labels don't overlap, but in above case the first and last labels overlapped. I have handled this case also in versions 1.4.32 onwards.
Please use the latest version of the library.

@GabiApc
Copy link

GabiApc commented Sep 12, 2024

image

Hi, @Abhinandan-Kushwaha. I used the same code above to make this chart and i noticed that when i have more than 9 elements the function avoidOverlappingOfLabels no longer works. I use 1.4.32 version of library

@Abhinandan-Kushwaha
Copy link
Owner

Hi @GabiApc 👋
avoidOverlappingOfLabels only avoids the overlapping of labels near the poles (top & bottom), because that is the region of most probable overlaps.
Other overlaps cannot be avoided. You need to increase the radius of the Pie chart to avoid other overlaps, or decrease the font size of labels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants