import React, { useEffect, useRef } from 'react'; import type { ScatterDataPoint } from 'chart.js'; import { CategoryScale, Chart, Filler, LinearScale, LineController, LineElement, PointElement, TimeScale, Tooltip, } from 'chart.js'; import 'chartjs-adapter-moment'; import { tailwindConfig } from '../../utils'; Chart.register( Filler, Tooltip, TimeScale, LineElement, LinearScale, PointElement, CategoryScale, LineController ); interface ILineChartProps { data: any; width: number; height: number; total?: number | string; } const LineChart = ({ data, width, height, total, }: ILineChartProps): JSX.Element => { const canvas: React.MutableRefObject = useRef(null); const legend: React.MutableRefObject = useRef(null); useEffect(() => { const ctx: any = window && canvas?.current; const chart: Chart< 'line', (number | ScatterDataPoint | null)[], unknown > | null = ctx ? new Chart(ctx, { type: 'line', data: data, options: { layout: { padding: 20 }, scales: { y: { beginAtZero: true, grid: { display: false }, ticks: { maxTicksLimit: 7, callback: value => `${value}` }, }, x: { grid: { display: false }, ticks: { autoSkipPadding: 48, maxRotation: 0 }, }, }, plugins: { legend: { display: false }, tooltip: { callbacks: { label: context => `${context.parsed.y}` }, }, }, interaction: { intersect: false, mode: 'nearest' }, maintainAspectRatio: false, // resizeDelay: 200, // fix breaking error }, plugins: [ { id: 'htmlLegend', afterUpdate(c) { const ul = legend.current; if (!ul) return; // Remove old legend items while (ul.firstChild) { ul.firstChild.remove(); } // Reuse the built-in legendItems generator if (c?.options.plugins?.legend?.labels?.generateLabels) { const items = c?.options?.plugins?.legend?.labels?.generateLabels(c); items.forEach(item => { const li = document?.createElement('li'); li.style.marginLeft = tailwindConfig().theme.margin[3]; // Button element const button = document?.createElement('button'); button.style.display = 'inline-flex'; button.style.alignItems = 'center'; button.style.opacity = item.hidden ? '.3' : ''; button.onclick = () => { c?.setDatasetVisibility( item.datasetIndex || 0, !c?.isDatasetVisible(item.datasetIndex || 0) ); c?.update(); }; // Color box const box = document?.createElement('span'); box.style.display = 'block'; box.style.width = tailwindConfig().theme.width[3]; box.style.height = tailwindConfig().theme.height[3]; box.style.borderRadius = tailwindConfig().theme.borderRadius.full; box.style.marginRight = tailwindConfig().theme.margin[2]; box.style.borderWidth = '3px'; box.style.borderColor = tailwindConfig().theme.colors.indigo[500]; box.style.pointerEvents = 'none'; const label = document?.createElement('span'); label.style.color = tailwindConfig().theme.colors.slate[500]; label.style.fontSize = tailwindConfig().theme.fontSize.sm[0]; label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight; const labelText = document?.createTextNode(item.text); label.appendChild(labelText); li.appendChild(button); button.appendChild(box); button.appendChild(label); ul.appendChild(li); }); } }, }, ], }) : null; return () => chart?.destroy(); }, []); return ( {total ? (

{Number(total).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2, })}

) : null} {/* Chart built with Chart.js 3 */}
); }; export default LineChart;