(https://i.stack.imgur.com/D98SY.png)
This is the initial view i am having. There will be a pie chart with 2 bar graphs.
(https://i.stack.imgur.com/hLALK.png)
In the above image when i am toggling the pie chart to circle chart you can see the total of the circle chart is displayed at the center which is what i require. But if you observe even in the bar charts too the total is getting displayed, also note that it is happening only when i am hovering. Also note that when i am retoggling circle chart to pie chart again if i hover on the bar chart , the displayed totals of the bar charts are vanishing.

My codes are:

PieChart.tsx

import React, { useRef, useEffect } from "react";
import { Doughnut, Pie } from "react-chartjs-2";
import {
  Chart as ChartJS,
  ArcElement,
  Tooltip,
  Legend,
  Chart,
  Title
} from "chart.js";
import { useContainerSize } from "../../../utils/containerResizeObserver";
import { PieChartProps } from "./PieChartInterface";
import { prepareChartData, determineLegendPosition, getFontSize } from "./PieChart";

ChartJS.register(ArcElement, Tooltip, Legend,Title);


const PieChart: React.FC<PieChartProps> = ({ data, valueRep, sideLabel, variant ,title}) => {
  const chartContainerRef = useRef<HTMLDivElement>(null);
  const containerSize = useContainerSize(chartContainerRef);

  const {
    chartDataValues,
    chartDataBackgroundColors,
    chartLabels,
    chartBorderWidths,
  } = prepareChartData(data);

  const chartData = {
    labels: chartLabels,
    datasets: [
      {
        data: chartDataValues,
        backgroundColor: chartDataBackgroundColors,
        borderColor: "#fff",
        borderWidth: chartBorderWidths,
      },
    ],
  };  

  const getLabelOffset = (variant: string | undefined) => variant === "pie" ? 1.5 : 1;

  const totalValuePlugin = {
    id: 'totalValuePlugin',
    afterDatasetsDraw(chart: { ctx: any; data: any; chartArea: any; }) {
      if (variant !== 'circle') return;
      
      const { ctx, data: chartData, chartArea } = chart;
      let totalValue = chartData.datasets[0].data.reduce((sum: any, value: any) => sum + value, 0);
      
      // Center position in chartArea, not entire canvas
      const centerX = chartArea.left + (chartArea.right - chartArea.left) / 2;
      const centerY = chartArea.top + (chartArea.bottom - chartArea.top) / 2;
      
      ctx.save();
      ctx.font = `${getFontSize(containerSize)} Arial`;
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText(`Total: ${totalValue}`, centerX, centerY);
      ctx.restore();
    }
  };
  

  const valueLabelPlugin = {
    id: 'valueLabelPlugin',
    afterDatasetsDraw(chart: { data?: any; getDatasetMeta?: any; ctx?: any; }) {
      const { ctx } = chart;
      ctx.save();
      chart.data.datasets.forEach((dataset: { data: { [x: string]: any; }; }, datasetIndex: any) => {
        const meta = chart.getDatasetMeta(datasetIndex);
        meta.data.forEach((element: { x?: any; y?: any; startAngle?: any; endAngle?: any; innerRadius?: any; outerRadius?: any; }, index: string | number) => {
          const value = dataset.data[index];
          const multiplier = getLabelOffset(variant);
          const { startAngle, endAngle, innerRadius, outerRadius } = element;
          const angle = startAngle + (endAngle - startAngle) / 2;
          const radius = (outerRadius + innerRadius) / 2 * multiplier;
          const labelX = element.x + radius * Math.cos(angle);
          const labelY = element.y + radius * Math.sin(angle);
          ctx.fillStyle = '#000';
          ctx.font = `${getFontSize(containerSize)} Arial`;
          ctx.textAlign = 'center';
          ctx.textBaseline = 'middle';
          ctx.fillText(value.toString(), labelX, labelY);
        });
      });
      ctx.restore();
    }
  };

  useEffect(() => {
    // Register the plugin only when the variant changes
    ChartJS.register(valueLabelPlugin, totalValuePlugin);
    return () => {
      // Unregister the plugin when the component unmounts or variant changes
      ChartJS.unregister(valueLabelPlugin, totalValuePlugin);
    };
  }, [variant]);


  // Chart options
  const chartOptions = {
    responsive: true,
    maintainAspectRatio: false,
    plugins: {
      tooltip: {
        callbacks: {
          label: function (context: { dataIndex: any; parsed: any; }) {
            let tooltipIndex = context.dataIndex;
            let valuePerSegment = valueRep.length;
            let labelIndex = tooltipIndex % valuePerSegment;
            let valueLabel = valueRep[labelIndex];
            let value = context.parsed;
            let subLabel = `${valueLabel}: ${value}`;
            return subLabel;
          }
        }
      },   
      title: {
        display: !!title?.display, // Display title only if title prop is provided and display is true
        text: `       ${title?.text || ''}`,  // Use title text if provided
        font: {
            size: 16, // Set default font size or adjust as needed
            weight: '400',
        },
        color: 'black',
        align: 'start',
        padding: {
          bottom: 4, // Adjust as needed
          top:8
      },
      },
      legend: {
        display: containerSize !== "nano" && containerSize !== "micro" && containerSize !== "small",
        position: determineLegendPosition(containerSize), // Assume this function returns the correct type
        labels: {
          generateLabels: function (chart: Chart) {
            const labels = chart.data.labels;
            const backgroundColor = chart.data.datasets[0].backgroundColor;
            if (!Array.isArray(labels) || !Array.isArray(backgroundColor)) {
              return [];
            }
            if (sideLabel) {
              return sideLabel.map((label) => ({
                text: label.name,
                fillStyle: label.color,
              }));
            } else {
              return labels.map((label, i) => ({
                text: label,
                fillStyle: backgroundColor[i],
              }));
            }
          },
          color: "#000",
          font: {
            size: getFontSize(containerSize),
          },
        },
      },
      totalValuePlugin: totalValuePlugin ? (containerSize === "medium" || containerSize === "large" || containerSize === "small" || containerSize === "micro") : NaN
    },
    
  };

  const ChartComponent = variant === 'circle' ? Doughnut : Pie;

  return (
    <div ref={chartContainerRef} style={{ height: "100%", width: "100%" }}>
      <ChartComponent data={chartData} options={chartOptions as any} />
    </div>
  );
};

export default PieChart;

PieChart.ts

import { PieChartData } from "./PieChartInterface";

export const determineLegendPosition = (containerSize: string): "top" | "right" =>{
    if (containerSize === "large" || containerSize === "medium" || containerSize === "micro") {
      return "right";
    } else {
      return "top";
    }
  }

export const getFontSize = (containerSize: string) => {
    switch(containerSize) {
      case 'nano':
        return '12px'; // Small font size for small containers
      case 'micro':
        return '14px';
      case 'small':
        return '16px';
      case 'medium':
        return '18px';
      case 'large':
        return '20px';
      case 'jumbo':
        return '22px'; // Larger font size for large containers
      default:
        return '16px'; // Default font size
    }
  };


export const prepareChartData = (data: PieChartData[]) => {
    const chartDataValues: number[] = [];
    const chartDataBackgroundColors: string[] = [];
    const chartLabels: string[] = [];
    const chartBorderWidths: number[] = [];
  
    data.forEach((segment) => {
      segment.values.forEach((value, index) => {
        chartDataValues.push(value);
        chartDataBackgroundColors.push(segment.colors[index]);
        chartLabels.push(segment.name);
      });
    });
  
    const borderWidthInterval = Math.max(1, Math.floor(chartDataValues.length / data.length));
    const defaultBorderWidth = 1;
    const differentiatedBorderWidth = 1;
  
    for (let i = 0; i < chartDataValues.length; i++) {
      const borderWidth = (i % borderWidthInterval === 0) ? differentiatedBorderWidth : defaultBorderWidth;
      chartBorderWidths.push(borderWidth);
    }
  
    return { chartDataValues, chartDataBackgroundColors, chartLabels, chartBorderWidths };
  };

PieChartInterface.ts

export type PieChartData = {
    name: string;
    values: number[];
    colors: string[];
  };
  
  export type SideLabel = {
    name: string;
    color: string;
  };
   export interface TitleProp {
    text: string;
    display: boolean;
  }
  
  export type PieChartProps = {
    data: PieChartData[];
    valueRep: string[];
    sideLabel?: { name: string; color: string }[];
    variant?: 'pie' | 'circle';
    title?: TitleProp;
  };

RFQCharts.tsx

import { Container } from "../../components/common/container/Container.tsx";
import "./RFQDashboard.scss";
import Barchart from "@/components/common/Chart/BarChart/BarChart.tsx";
import PieChartWrapper from './PieChartWrapper.tsx';

export const RFQCharts = () => {

  const barData={
    labels: ['Complete1', 'Complete2', 'Complete3', 'Complete4', 'Complete5', 'Complete1', 'Complete2', 'Complete3', 'Complete4', 'complete5'],
    datasets : [
      {
        label: 'Category1',
        data: [90, 50, 45, 20, 45, 70, 25, 40, 45, 50],
        backgroundColor: 'lightcoral',
        borderColor: 'darkred',
        borderWidth: 1,
      },
      {
        label: 'Category2',
        data: [70, 42, 68, 25, 30, 50, 35, 55, 60, 65],
        backgroundColor: 'lightgreen',
        borderColor: 'darkgreen',
        borderWidth: 1,
      },
      {
        label: 'Category3',
        data: [60, 35, 70, 30, 35, 60, 40, 70, 50, 80],
        backgroundColor: 'lightblue',
        borderColor: 'darkblue',
        borderWidth: 1,
      },
    ]
  };

  const data = [
    { name: "AOG", values: [90, 10], colors: ["#2A9DF4", "#00FFFF"] },
    { name: "Critical", values: [40, 60], colors: ["#2A9DF4", "#00FFFF"] },
    { name: "Expedite", values: [30, 70], colors: ["#2A9DF4", "#00FFFF"] },
    { name: "Routine", values: [50, 50], colors: ["#2A9DF4", "#00FFFF"] }
  ];

  return (
    <Container className="flex-container chartContainer">
      <h1 className="heading">RFQ Dashboard</h1>
      <Container className="twoGraphs">
        <Container className="barChart1">
          <Barchart
            data={barData}
            xAxisLabel="Days"
            yAxisLabel="Values"
            barThickness={35}
          />
        </Container>
        
           <PieChartWrapper data={data} />
    
      </Container>
      <Container className="barChart2">
        <Barchart
          data={barData}
          xAxisLabel="Days"
          yAxisLabel="Values"
          barThickness={35} />
      </Container>
    </Container>
  );
};

PieChartWrapper.tsx

import { useState } from 'react';
import PieChart from '@/components/common/PieChart/PieChart.tsx';
import ToggleButton from '@/components/ui/button-toggle/ToggleButton';
import { Container } from '@/components/common/container/Container';

// Define the type for a single data item
interface DataItem {
  name: string;
  values: number[];
  colors: string[];
}

// Define the props for the PieChartWrapper component
interface PieChartWrapperProps {
  data: DataItem[];
}

const PieChartWrapper: React.FC<PieChartWrapperProps> = ({ data }) => {
  const [chartVariant, setChartVariant] = useState<"pie" | "circle">("pie");

  // Handle toggle change to switch between pie and circle chart
    const handleToggleChange = (isToggled: boolean) => {
    setChartVariant(isToggled ? "circle" : "pie");
  };

  return (
    <Container className="rfqPieChart">
      <ToggleButton className="pieChartToggle" onToggle={handleToggleChange} />
      <PieChart
        data={data}
        valueRep={["processed", "unprocessed"]}
        sideLabel={[
          { name: "processed", color: "#2A9DF4" },
          { name: "unprocessed", color: "#00FFFF" }
        ]}
        variant={chartVariant}
      />
  </Container>
  );
};

export default PieChartWrapper;

BarChart.tsx

import React, { useState, useEffect, useRef } from 'react';
import { Bar } from 'react-chartjs-2';
import { Chart as chartjs, BarElement, CategoryScale, LinearScale, Tooltip, Legend, Chart } from 'chart.js';
import 'chartjs-adapter-moment';
import { BarChartOptions, BarProps } from './BarChart';
import { useContainerSize } from "../../../../utils/containerResizeObserver";
import CircularProgress from '@mui/material/CircularProgress';
import Box from '@mui/material/Box';
import { styled } from '@mui/system';

chartjs.register(
  BarElement,
  CategoryScale,
  LinearScale,
  Tooltip,
  Legend,
);

// Styled Box component to wrap the CircularProgress
const CustomBox = styled(Box)(({ }) => ({
  position: 'absolute',
  top: '50%',
  left: '50%',
  transform: 'translate(-50%, -50%)',
}));

// Styled CircularProgress component with custom styles
const CustomCircularProgress = styled(CircularProgress)(({ }) => ({
  '& svg': {
    color: 'black', // Set the color of the SVG to black
    height: '2.5rem', // Set the height of the SVG
    width: '2.5rem', // Set the width of the SVG
  },
}));

const BarChart: React.FC<BarProps> = ({
  data,
  chartTitle,
  xAxisLabel,
  yAxisLabel,
  barThickness = 25,
}) => {
  const [initialLabels, setInitialLabels] = useState<string[]>([]);
  const [showSpinner, setShowSpinner] = useState(true); // Initially show spinner
  const chartContainerRef = useRef<HTMLDivElement>(null);
  const containerSize = useContainerSize(chartContainerRef);

  useEffect(() => {
    setShowSpinner(true); // Show spinner when container size changes
    // Simulate asynchronous data fetching
    const fetchData = () => {
      setTimeout(() => {
        const numBars = determineNumBars(containerSize);
        setInitialLabels(data.labels.slice(0, numBars));
        setShowSpinner(false); // Hide spinner after setting initial labels
      }, 2000); // Simulate delay with setTimeout
    };

    fetchData();
  }, [containerSize, data.labels]); // Trigger effect when containerSize or labelsComplete change

  const determineNumBars = (containerSize: string): number => {
    const sizeMapping: { [key: string]: number } = {
      large: data.labels.length,
      medium: 7,
      small: 5,
      micro: 3,
    };

    return Math.min(sizeMapping[containerSize] || 5, data.labels.length);
  };

  const modifiedData = {
    labels: initialLabels,
    datasets: data.datasets.map((dataset) => ({
      ...dataset,
      barThickness: barThickness
    })),
  };

  const stackOption = {
    ...BarChartOptions(xAxisLabel, yAxisLabel),
    maintainAspectRatio:false,
    responsive: true,
  };

  const dataLabelPlugin = {
    id: 'dataLabelPlugin',
    afterDatasetsDraw: (chart: Chart) => {
      const ctx = chart.ctx;
      chart.data.datasets.forEach((dataset: any, i: number) => { // Update this line
        const meta = chart.getDatasetMeta(i);
        if (!meta.hidden) {
          meta.data.forEach((element: any, index: number) => {
            ctx.fillStyle = 'black';
            const fontSize = 12;
            const fontStyle = 'normal';
            const fontFamily = 'Helvetica Neue, Helvetica, Arial, sans-serif';
            ctx.font = `${fontStyle} ${fontSize}px ${fontFamily}`;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            const dataString = dataset.data[index].toString(); // Update this line
            const yPos = element.y + (element.height / 2); // Adjust y-coordinate to center text vertically
            ctx.fillText(dataString, element.x, yPos);
          });
        }
      });
    }
  };
  // Register the custom plugin
  chartjs.register(dataLabelPlugin);

  return (
    <div ref={chartContainerRef} style={{ height: "100%", width: "100%", position: 'relative' }}>
       <h2>{chartTitle}</h2> 
      {showSpinner && ( // Show spinner only if it's true
        <CustomBox>
          <CustomCircularProgress /> {/* Use the custom styled CircularProgress component */}
        </CustomBox>
      )}
      <Bar data={modifiedData} options={stackOption} />
    </div>
  );
};

export default BarChart;

I want the total of only circle chart and it should be displayed at the center space of the circle chart. No other charts should get that total plugin. Even the pie chart also should not get the total.

New contributor

320127E11 ARAGHAVENDRAGOKULALU is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.