I need to create some bar chart.
This is a reference:
https://i.sstatic.net/Zj4G3BmS.png
This is what I have for now:
https://i.sstatic.net/pzPxKaZf.png
I marked my 3 questions on the reference with numbers:
- How to add a custom block to the chart legend, like in the reference?
- How to add such red blocks to the chart itself above the bars?
- How to add padding between bars? – EDIT: Fix this. See comments in code section.
Regarding point 3 – I read about barPercentage
and categoryPercentage
and they did not help me.
Project stack:
- Vue 3.4
- Chart.js 4.4.2
- Vue-chartjs 5.3.1
This is my component:
<template>
<div
class="bg-white rounded-md py-[12px] px-[20px] h-full flex items-center justify-center"
>
<Bar
v-if="planFact"
id="planfact-chart"
:options="chartOptions"
:data="(chartData as ChartData<'bar'>)"
:plugins="[plugin]"
/>
<div v-else>No data</div>
</div>
</template>
<script setup lang="ts">
import { Bar, type ChartProps } from 'vue-chartjs';
import type { ChartData } from 'chart.js';
import { useChartStore } from '@/stores/chartStore';
const chartStore = useChartStore();
const { planFact, shortedPlanFactLabels } = storeToRefs(chartStore);
// https://github.com/chartjs/Chart.js/issues/10388#issuecomment-1217363379
const plugin = {
id: 'increase-legend-spacing',
beforeInit(chart: any) {
// Get reference to the original fit function
const originalFit = chart.legend.fit;
// Override the fit function
chart.legend.fit = function fit() {
// Call original function and bind scope in order to use `this` correctly inside it
originalFit.bind(chart.legend)();
// Change the height as suggested in another answers
this.height += 15;
};
},
};
const chartOptions: ChartProps<'bar'>['options'] = {
responsive: true,
maintainAspectRatio: false,
layout: {
padding: {
top: -10,
},
autoPadding: true,
},
scales: {
y: {
ticks: {
color: '#768295',
stepSize: 50,
font: {
family: 'Noto Sans',
size: 12,
},
},
grid: {
color: '#EBF0F4',
},
border: {
color: '#EBF0F4',
},
},
x: {
ticks: {
color: '#768295',
font: {
family: 'Noto Sans',
size: 12,
},
},
border: {
color: '#EBF0F4',
},
grid: {
color: '#EBF0F4',
},
},
},
plugins: {
legend: {
display: true,
align: 'start',
labels: {
color: '#48546A',
padding: 15,
usePointStyle: true,
pointStyle: 'circle',
boxHeight: 5,
font: {
family: 'Noto Sans',
size: 14,
},
},
},
tooltip: {
enabled: false,
},
},
};
const chartData: ChartData<'bar'> = {
labels: shortedPlanFactLabels.value || [],
datasets: [
{
label: 'Fact',
data: planFact.value?.fact || [],
backgroundColor: ['#A594FF'],
borderColor: '#A594FF',
borderRadius: 3,
// EDIT: need to remove barThickness and then barPercentage and categoryPercentage will work
barThickness: 15,
barPercentage: 1.0,
categoryPercentage: 1.0,
stack: 'bar',
},
{
label: 'Plan',
data: planFact.value?.plan || [],
backgroundColor: ['#8792A3'],
borderSkipped: 'bottom',
borderRadius: 3,
// EDIT: need to remove barThickness and then barPercentage and categoryPercentage will work
barThickness: 15,
barPercentage: 1.0,
categoryPercentage: 1.0,
},
],
};
</script>
For the third point, I tried using barPercentage
and categoryPercentage
, but it didn’t help me, for the rest I didn’t even find examples of something similar