React Chartjs - Stacked bar chart, bars behind each other

126 Views Asked by At

i'm having a problem with my stacked chart, as you can see in my first image, and second image, some bars is starting in the middle of other bars making my datalabels beeing invading other bar.

If you look the first image you will se two bars stacked, the first colum you can see that all bars starts when the previus bar ends, but in the second column the third stacked bar starts in the middle of second bar, making the unusual layout as you can see on the second image, and it only happens if the value is 0 what could I do to prevent it and allways start a new bar inthe end of previus bar

First Image Second image

Here is my source code, the version of my Chartjs is 4.2.1

import { EChartTheme } from '@/enums'
import { numberFormat } from '@/helpers'
import { ChartData, ChartDataset, ChartOptions } from 'chart.js'
import { RefObject } from 'react'
import { Chart as PrimeChart } from 'primereact/chart'

interface DailyMapChartProps {
    chartRef: RefObject<PrimeChart>
    lineNumbers: number[]
    chartBottomLabels: string[][]
    chartTopLabels: string[]
    maxColumnsPerView: number

    freeTimeData: number[]
    markedTimeData: number[]
    bloquedTimeData: number[]
}

export const useDailyMapChart = ({
    chartRef,
    lineNumbers,
    chartBottomLabels,
    chartTopLabels,
    maxColumnsPerView,
    bloquedTimeData,
    freeTimeData,
    markedTimeData,
}: DailyMapChartProps) => {
    const findNearestTo5 = (numbers: number[]): number => {
        // Initialize variables to keep track of the closest number and its difference from 5.0
        let closestNumber: number | null = null
        let minDifference: number = Infinity

        // Iterate through the array of numbers
        for (const num of numbers) {
            // Calculate the absolute difference between the current number and 5.0
            const difference: number = Math.abs(num - 5.0)

            // Check if the current number is closer to 5.0 than the previous closest number
            if (difference < minDifference) {
                closestNumber = num
                minDifference = difference
            }
        }

        // Return the closest number to 5.0
        return closestNumber!
    }

    const nearestToFiveNumer = findNearestTo5(lineNumbers)

    let newData: ChartDataset[] = [
        {
            data: freeTimeData,
            backgroundColor: EChartTheme.LIGHT_GREEN_BAR,
            barThickness: 30,
            minBarLength: 25,
        },
        {
            data: markedTimeData,
            backgroundColor: EChartTheme.LIGHT_BLUE_BAR,
            barThickness: 30,
            minBarLength: 25,
        },
        {
            data: bloquedTimeData,
            backgroundColor: EChartTheme.ORANGE_BAR,
            barThickness: 30,
            minBarLength: 25,
            borderRadius: 2,
        },
        {
            data: lineNumbers,
            borderColor: EChartTheme.LIGHT_RED_BAR,
            type: 'line',
            tension: 0.3,
            datalabels: {
                align: 'right',
                offset: 8,
                borderRadius: 2,
                font: {
                    size: 11,
                },
                backgroundColor: EChartTheme.LIGHT_GRAY,
                padding: 2,
                borderWidth: 2,
                borderColor: (context: any) => {
                    const currentNumber = lineNumbers[context.dataIndex]
                    if (currentNumber == nearestToFiveNumer) return EChartTheme.RED_BAR
                    return ''
                },
                formatter(value, context) {
                    const formated = numberFormat(value, 1)
                    return `${formated.replace(',', '.')}%`
                },
            },
        },
    ]

    const data: ChartData = {
        labels: chartBottomLabels,
        datasets: newData,
    }

    const options: ChartOptions = {
        maintainAspectRatio: false,
        plugins: {
            datalabels: {
                anchor: 'end',
                align: 'end',
                offset: -25,
                font: {
                    size: 12,
                    weight: 'bold',
                },
            },
            legend: {
                display: false,
            },
            tooltip: {
                backgroundColor: EChartTheme.TOOLTIP_BACKGROUND,
                callbacks: {
                    title: tooltipItems => tooltipItems[0].label.replaceAll(',', ' - '),
                },
            },
        },

        scales: {
            x: {
                beginAtZero: true,
                stacked: true,
                grid: {
                    display: false,
                },
                min: 0,
                max: maxColumnsPerView,
            },
            y: {
                stacked: true,
                beginAtZero: true,
                grid: {
                    display: false,
                },
                ticks: {
                    display: false,
                },
                border: {
                    display: false,
                },
            },
            secondYAxis: {
                axis: 'x',
                position: 'top',
                labels: chartTopLabels,
                min: 0,
                max: maxColumnsPerView,
                ticks: {
                    font: {
                        size: 14,
                        weight: 'bold',
                    },
                },
                grid: {
                    display: false,
                },

                border: {
                    display: false,
                },
            },
        },
    }

    // ChartScroll with mouse wheel
    const scroller = (scrollEvent: WheelEvent) => {
        const { deltaY } = scrollEvent

        scrollEvent.preventDefault()

        if (data.labels!.length < maxColumnsPerView) return

        if (deltaY > 0 && chartRef != null && chartRef.current != null) {
            const chart = chartRef.current.getChart()
            const options = chartRef.current.props.options as any

            if (options.scales.x.min == data.labels!.length - maxColumnsPerView - 1) {
                return
            }

            if (options.scales.x.max >= data.labels!.length) return

            options.scales.x.min += 1
            options.scales.x.max += 1
            options.scales.secondYAxis.min += 1
            options.scales.secondYAxis.max += 1

            chart.update()
        } else if (deltaY < 0 && chartRef != null && chartRef.current != null) {
            const chart = chartRef.current.getChart()
            const options = chartRef.current.props.options as any

            if (options.scales.x.min == 0) return

            options.scales.x.min -= 1
            options.scales.x.max -= 1
            options.scales.secondYAxis.min -= 1
            options.scales.secondYAxis.max -= 1

            chart.update()
        }
    }

    // ChartScroll with touch click
    const touchChartChange = (event: TouchEvent) => {
        const screenY = event.touches[0].screenY
        const clickPosition = event.touches[0].clientX

        if (data.labels!.length < maxColumnsPerView) return

        if (chartRef != null && chartRef.current != null) {
            // Se o click deu menos que a metade da tela entao ele clickou na esquerda
            if (clickPosition < screenY / 2) {
                const chart = chartRef.current.getChart()
                const options = chartRef.current.props.options as any

                if (options.scales.x.min == 0) return

                options.scales.x.min -= 1
                options.scales.x.max -= 1
                options.scales.secondYAxis.min -= 1
                options.scales.secondYAxis.max -= 1

                chart.update()
            } else {
                const chart = chartRef.current.getChart()
                const options = chartRef.current.props.options as any

                if (
                    options.scales.x.min ==
                    data.labels!.length - maxColumnsPerView - 1
                ) {
                    return
                }

                if (options.scales.x.max >= data.labels!.length) return

                options.scales.x.min += 1
                options.scales.x.max += 1
                options.scales.secondYAxis.min += 1
                options.scales.secondYAxis.max += 1

                chart.update()
            }
        }
    }

    const addListeners = () => {
        const chart = chartRef?.current?.getCanvas()
        chart?.addEventListener('wheel', scroller, { once: true })
        chart?.addEventListener('touchstart', touchChartChange, { once: true })
    }

    return {
        data,
        options,
        addListeners,
    }
}

I tred to change the stacked properties and beginAtZero true and false

0

There are 0 best solutions below