Files
ppeasy_vg12/resources/js/components/LineChart.vue
T
2026-10-09 08:21:06 +02:00

118 lines
2.8 KiB
Vue

<script setup lang="ts">
import {
CategoryScale,
Chart,
Filler,
LinearScale,
LineController,
LineElement,
PointElement,
Tooltip,
type ChartConfiguration,
} from 'chart.js';
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
const props = withDefaults(
defineProps<{
labels: string[];
values: number[];
/** Series label, shown in the tooltip title area. */
label?: string;
/** Builds the tooltip text for a value, e.g. "3 actions". */
formatValue?: (value: number) => string;
height?: number;
}>(),
{ height: 365 },
);
Chart.register(
LineController,
LineElement,
PointElement,
LinearScale,
CategoryScale,
Filler,
Tooltip,
);
const canvas = ref<HTMLCanvasElement | null>(null);
let chart: Chart | null = null;
const BRAND = '#179970';
function config(): ChartConfiguration {
return {
type: 'line',
data: {
labels: props.labels,
datasets: [
{
label: props.label ?? '',
backgroundColor: 'transparent',
borderColor: BRAND,
pointBackgroundColor: BRAND,
data: props.values,
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: { grid: { display: false } },
y: {
beginAtZero: true,
grid: { color: '#f6f6f6' },
border: { display: false },
// Registrations and activity counts are whole numbers.
ticks: { precision: 0 },
},
},
plugins: {
legend: { display: false },
tooltip: {
titleMarginBottom: 15,
callbacks: {
label: (item) =>
props.formatValue
? ` ${props.formatValue(Number(item.parsed.y))}`
: ` ${item.parsed.y}`,
},
},
},
},
};
}
onMounted(() => {
if (canvas.value) {
chart = new Chart(canvas.value, config());
}
});
watch(
() => [props.labels, props.values],
() => {
if (!chart) {
return;
}
chart.data.labels = props.labels;
chart.data.datasets[0].data = props.values;
chart.update();
},
{ deep: true },
);
onBeforeUnmount(() => {
chart?.destroy();
chart = null;
});
</script>
<template>
<div :style="{ height: `${height}px` }">
<canvas ref="canvas" />
</div>
</template>