first commit

This commit is contained in:
kandrusyak
2022-05-23 12:56:31 +03:00
commit 3cea343aaf
395 changed files with 37099 additions and 0 deletions

162
src/charts/BarChart01.jsx Normal file
View File

@@ -0,0 +1,162 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart01({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.marginRight = tailwindConfig().theme.margin[4];
// 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, !c.isDatasetVisible(item.datasetIndex));
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 = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
const labelContainer = document.createElement('span');
labelContainer.style.display = 'flex';
labelContainer.style.alignItems = 'center';
const value = document.createElement('span');
value.style.color = tailwindConfig().theme.colors.slate[800];
value.style.fontSize = tailwindConfig().theme.fontSize['3xl'][0];
value.style.lineHeight = tailwindConfig().theme.fontSize['3xl'][1].lineHeight;
value.style.fontWeight = tailwindConfig().theme.fontWeight.bold;
value.style.marginRight = tailwindConfig().theme.margin[2];
value.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 theValue = c.data.datasets[item.datasetIndex].data.reduce((a, b) => a + b, 0);
const valueText = document.createTextNode(formatValue(theValue));
const labelText = document.createTextNode(item.text);
value.appendChild(valueText);
label.appendChild(labelText);
li.appendChild(button);
button.appendChild(box);
button.appendChild(labelContainer);
labelContainer.appendChild(value);
labelContainer.appendChild(label);
ul.appendChild(li);
});
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-3">
<ul ref={legend} className="flex flex-wrap"></ul>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart01;

99
src/charts/BarChart02.jsx Normal file
View File

@@ -0,0 +1,99 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart02({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
stacked: true,
grid: {
drawBorder: false,
},
beginAtZero: true,
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
stacked: true,
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 200,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default BarChart02;

156
src/charts/BarChart03.jsx Normal file
View File

@@ -0,0 +1,156 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatThousands } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart03({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
stacked: true,
grid: {
drawBorder: false,
},
beginAtZero: true,
ticks: {
maxTicksLimit: 5,
callback: (value) => formatThousands(value),
},
},
x: {
stacked: true,
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatThousands(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current
if (!ul) return
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove()
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c)
items.forEach((item) => {
const li = document.createElement('li')
li.style.marginRight = 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, !c.isDatasetVisible(item.datasetIndex))
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 = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
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)
})
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-4">
<div className="grow mb-1">
<ul ref={legend} className="flex flex-wrap"></ul>
</div>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart03;

149
src/charts/BarChart04.jsx Normal file
View File

@@ -0,0 +1,149 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatThousands } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart04({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
indexAxis: 'y',
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM',
},
},
grid: {
display: false,
drawBorder: false,
},
},
x: {
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 3,
align: 'end',
callback: (value) => formatThousands(value),
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatThousands(context.parsed.x),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current
if (!ul) return
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove()
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c)
items.forEach((item) => {
const li = document.createElement('li')
li.style.marginRight = tailwindConfig().theme.margin[4]
// 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, !c.isDatasetVisible(item.datasetIndex))
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 = item.fillStyle
box.style.pointerEvents = 'none'
// Label
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)
})
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-4">
<ul ref={legend} className="flex flex-wrap"></ul>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart04;

157
src/charts/BarChart05.jsx Normal file
View File

@@ -0,0 +1,157 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart05({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [
{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.marginRight = tailwindConfig().theme.margin[4];
// 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, !c.isDatasetVisible(item.datasetIndex));
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 = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
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);
});
},
},
],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-3">
<div className="flex flex-wrap justify-between items-center">
<div className="flex items-center">
<div className="text-3xl font-bold text-slate-800 mr-2">$1,347.09</div>
<div className="text-sm">Net</div>
</div>
<div className="grow ml-2">
<ul ref={legend} className="flex flex-wrap justify-end"></ul>
</div>
</div>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart05;

164
src/charts/BarChart06.jsx Normal file
View File

@@ -0,0 +1,164 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, CategoryScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
// Import images
import revolutIcon from '../images/company-icon-01.svg';
import hsbcIcon from '../images/company-icon-02.svg';
import qontoIcon from '../images/company-icon-03.svg';
import n26Icon from '../images/company-icon-04.svg';
Chart.register(BarController, BarElement, LinearScale, CategoryScale, Tooltip, Legend);
const images = [revolutIcon, hsbcIcon, qontoIcon, n26Icon];
function BarChart06({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
indexAxis: 'y',
layout: {
padding: {
top: 12,
bottom: 16,
left: 72,
right: 20,
},
},
scales: {
y: {
grid: {
display: false,
drawBorder: false,
drawTicks: false,
},
ticks: {
display: false,
},
},
x: {
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 3,
align: 'end',
callback: (value) => formatValue(value),
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.x),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [
{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.marginRight = tailwindConfig().theme.margin[4];
// 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, !c.isDatasetVisible(item.datasetIndex));
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 = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
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);
});
},
afterDraw(c) {
const xAxis = c.scales.x;
const yAxis = c.scales.y;
yAxis.ticks.forEach((value, index) => {
const y = yAxis.getPixelForTick(index);
const image = new Image();
image.src = images[index];
c.ctx.drawImage(image, xAxis.left - 52, y - 18);
});
},
},
],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-4">
<ul ref={legend} className="flex flex-wrap"></ul>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart06;

View File

@@ -0,0 +1,42 @@
// Import Chart.js
import { Chart, Tooltip } from 'chart.js';
// Import Tailwind config
import { tailwindConfig } from '../utils/Utils';
Chart.register(Tooltip);
// Define Chart.js default settings
Chart.defaults.font.family = '"Inter", sans-serif';
Chart.defaults.font.weight = '500';
Chart.defaults.color = tailwindConfig().theme.colors.slate[400];
Chart.defaults.scale.grid.color = tailwindConfig().theme.colors.slate[100];
Chart.defaults.plugins.tooltip.titleColor = tailwindConfig().theme.colors.slate[800];
Chart.defaults.plugins.tooltip.bodyColor = tailwindConfig().theme.colors.slate[800];
Chart.defaults.plugins.tooltip.backgroundColor = tailwindConfig().theme.colors.white;
Chart.defaults.plugins.tooltip.borderWidth = 1;
Chart.defaults.plugins.tooltip.borderColor = tailwindConfig().theme.colors.slate[200];
Chart.defaults.plugins.tooltip.displayColors = false;
Chart.defaults.plugins.tooltip.mode = 'nearest';
Chart.defaults.plugins.tooltip.intersect = false;
Chart.defaults.plugins.tooltip.position = 'nearest';
Chart.defaults.plugins.tooltip.caretSize = 0;
Chart.defaults.plugins.tooltip.caretPadding = 20;
Chart.defaults.plugins.tooltip.cornerRadius = 4;
Chart.defaults.plugins.tooltip.padding = 8;
// Register Chart.js plugin to add a bg option for chart area
Chart.register({
id: 'chartAreaPlugin',
// eslint-disable-next-line object-shorthand
beforeDraw: (chart) => {
if (chart.config.options.chartArea && chart.config.options.chartArea.backgroundColor) {
const ctx = chart.canvas.getContext('2d');
const { chartArea } = chart;
ctx.save();
ctx.fillStyle = chart.config.options.chartArea.backgroundColor;
// eslint-disable-next-line max-len
ctx.fillRect(chartArea.left, chartArea.top, chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
ctx.restore();
}
},
});

View File

@@ -0,0 +1,114 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, DoughnutController, ArcElement, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig } from '../utils/Utils';
Chart.register(DoughnutController, ArcElement, TimeScale, Tooltip);
function DoughnutChart({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'doughnut',
data: data,
options: {
cutout: '80%',
layout: {
padding: 24,
},
plugins: {
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.margin = tailwindConfig().theme.margin[1];
// Button element
const button = document.createElement('button');
button.classList.add('btn-xs');
button.style.backgroundColor = tailwindConfig().theme.colors.white;
button.style.borderWidth = tailwindConfig().theme.borderWidth[1];
button.style.borderColor = tailwindConfig().theme.colors.slate[200];
button.style.color = tailwindConfig().theme.colors.slate[500];
button.style.boxShadow = tailwindConfig().theme.boxShadow.md;
button.style.opacity = item.hidden ? '.3' : '';
button.onclick = () => {
c.toggleDataVisibility(item.index, !item.index);
c.update();
};
// Color box
const box = document.createElement('span');
box.style.display = 'block';
box.style.width = tailwindConfig().theme.width[2];
box.style.height = tailwindConfig().theme.height[2];
box.style.backgroundColor = item.fillStyle;
box.style.borderRadius = tailwindConfig().theme.borderRadius.sm;
box.style.marginRight = tailwindConfig().theme.margin[1];
box.style.pointerEvents = 'none';
// Label
const label = document.createElement('span');
label.style.display = 'flex';
label.style.alignItems = 'center';
const labelText = document.createTextNode(item.text);
label.appendChild(labelText);
li.appendChild(button);
button.appendChild(box);
button.appendChild(label);
ul.appendChild(li);
});
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="grow flex flex-col justify-center">
<div>
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
<div className="px-5 pt-2 pb-6">
<ul ref={legend} className="flex flex-wrap justify-center -m-1"></ul>
</div>
</div>
);
}
export default DoughnutChart;

View File

@@ -0,0 +1,76 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart01({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
chartArea: {
backgroundColor: tailwindConfig().theme.colors.slate[50],
},
layout: {
padding: 20,
},
scales: {
y: {
display: false,
beginAtZero: true,
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
},
display: false,
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart01;

153
src/charts/LineChart02.jsx Normal file
View File

@@ -0,0 +1,153 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart02({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: 20,
},
scales: {
y: {
grid: {
drawBorder: false,
beginAtZero: true,
},
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.slice(0, 2).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, !c.isDatasetVisible(item.datasetIndex));
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 = c.data.datasets[item.datasetIndex].borderColor;
box.style.pointerEvents = 'none';
// Label
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);
});
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-3">
<div className="flex flex-wrap justify-between items-end">
<div className="flex items-start">
<div className="text-3xl font-bold text-slate-800 mr-2">$1,482</div>
<div className="text-sm font-semibold text-white px-1.5 bg-amber-500 rounded-full">-22%</div>
</div>
<div className="grow ml-2 mb-1">
<ul ref={legend} className="flex flex-wrap justify-end"></ul>
</div>
</div>
</div>
{/* Chart built with Chart.js 3 */}
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default LineChart02;

View File

@@ -0,0 +1,88 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatThousands } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart03({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: 20,
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
},
ticks: {
callback: (value) => formatThousands(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatThousands(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart03;

View File

@@ -0,0 +1,78 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatThousands } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart04({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
chartArea: {
backgroundColor: tailwindConfig().theme.colors.slate[50],
},
layout: {
padding: {
left: 20,
right: 20,
},
},
scales: {
y: {
display: false,
beginAtZero: true,
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
},
display: false,
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatThousands(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart04;

157
src/charts/LineChart05.jsx Normal file
View File

@@ -0,0 +1,157 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart05({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: 20,
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 7,
callback: (value) => `${value}%`,
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => `${context.parsed.y}%`,
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [
{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
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, !c.isDatasetVisible(item.datasetIndex));
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 = c.data.datasets[item.datasetIndex].borderColor;
box.style.pointerEvents = 'none';
// Label
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);
});
},
},
],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-3">
<div className="flex flex-wrap justify-between items-end">
<div className="flex items-center">
<div className="text-3xl font-bold text-slate-800 mr-2">244.7%</div>
<div className="text-sm">
<span className="font-medium text-slate-800">17.4%</span> AVG
</div>
</div>
<div className="grow ml-2 mb-1">
<ul ref={legend} className="flex flex-wrap justify-end" />
</div>
</div>
</div>
{/* Chart built with Chart.js 3 */}
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default LineChart05;

View File

@@ -0,0 +1,94 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart06({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 7,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart06;

View File

@@ -0,0 +1,86 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, CategoryScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, CategoryScale, Tooltip);
function LineChart07({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 7,
callback: (value) => formatValue(value),
},
},
x: {
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
align: 'end',
},
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart07;

View File

@@ -0,0 +1,84 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart08({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: {
top: 16,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
drawTicks: false,
},
ticks: {
maxTicksLimit: 2,
display: false,
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
},
display: false,
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart08;

View File

@@ -0,0 +1,69 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart09({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
scales: {
y: {
display: false,
beginAtZero: true,
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
},
display: false,
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart09;

117
src/charts/PieChart.jsx Normal file
View File

@@ -0,0 +1,117 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, PieController, ArcElement, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig } from '../utils/Utils';
Chart.register(PieController, ArcElement, TimeScale, Tooltip);
function PieChart({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'pie',
data: data,
options: {
layout: {
padding: {
top: 4,
bottom: 4,
left: 24,
right: 24,
},
},
plugins: {
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 200,
},
maintainAspectRatio: false,
},
plugins: [
{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.margin = tailwindConfig().theme.margin[1.5];
// 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.toggleDataVisibility(item.index, !item.index);
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[1.5];
box.style.borderWidth = '3px';
box.style.borderColor = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
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);
});
},
},
],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="grow flex flex-col justify-center">
<div>
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
<div className="px-5 py-4">
<ul ref={legend} className="flex flex-wrap justify-center -m-1" />
</div>
</div>
);
}
export default PieChart;

113
src/charts/PolarChart.jsx Normal file
View File

@@ -0,0 +1,113 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, PolarAreaController, RadialLinearScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig } from '../utils/Utils';
Chart.register(PolarAreaController, RadialLinearScale, Tooltip, Legend);
function PolarChart({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'polarArea',
data: data,
options: {
layout: {
padding: 24,
},
plugins: {
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current
if (!ul) return
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove()
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c)
items.forEach((item) => {
const li = document.createElement('li')
li.style.margin = tailwindConfig().theme.margin[1]
// Button element
const button = document.createElement('button')
button.classList.add('btn-xs')
button.style.backgroundColor = tailwindConfig().theme.colors.white
button.style.borderWidth = tailwindConfig().theme.borderWidth[1]
button.style.borderColor = tailwindConfig().theme.colors.slate[200]
button.style.color = tailwindConfig().theme.colors.slate[500]
button.style.boxShadow = tailwindConfig().theme.boxShadow.md
button.style.opacity = item.hidden ? '.3' : ''
button.onclick = () => {
c.toggleDataVisibility(item.index, !item.index)
c.update()
}
// Color box
const box = document.createElement('span')
box.style.display = 'block'
box.style.width = tailwindConfig().theme.width[2]
box.style.height = tailwindConfig().theme.height[2]
box.style.backgroundColor = item.fillStyle
box.style.borderRadius = tailwindConfig().theme.borderRadius.sm
box.style.marginRight = tailwindConfig().theme.margin[1]
box.style.pointerEvents = 'none'
// Label
const label = document.createElement('span')
label.style.display = 'flex'
label.style.alignItems = 'center'
const labelText = document.createTextNode(item.text)
label.appendChild(labelText)
li.appendChild(button)
button.appendChild(box)
button.appendChild(label)
ul.appendChild(li)
})
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="grow flex flex-col justify-center">
<div>
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
<div className="px-5 pt-2 pb-6">
<ul ref={legend} className="flex flex-wrap justify-center -m-1"></ul>
</div>
</div>
);
}
export default PolarChart;

View File

@@ -0,0 +1,120 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function RealtimeChart({
data,
width,
height
}) {
const canvas = useRef(null);
const chartValue = useRef(null);
const chartDeviation = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: 20,
},
scales: {
y: {
grid: {
drawBorder: false,
},
suggestedMin: 30,
suggestedMax: 80,
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'hh:mm:ss',
unit: 'second',
tooltipFormat: 'MMM DD, H:mm:ss a',
displayFormats: {
second: 'H:mm:ss',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
titleFont: {
weight: '600',
},
callbacks: {
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: false,
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data]);
// Update header values
useEffect(() => {
const currentValue = data.datasets[0].data[data.datasets[0].data.length - 1];
const previousValue = data.datasets[0].data[data.datasets[0].data.length - 2];
const diff = ((currentValue - previousValue) / previousValue) * 100;
chartValue.current.innerHTML = data.datasets[0].data[data.datasets[0].data.length - 1];
if (diff < 0) {
chartDeviation.current.style.backgroundColor = tailwindConfig().theme.colors.yellow[500];
} else {
chartDeviation.current.style.backgroundColor = tailwindConfig().theme.colors.emerald[500];
}
chartDeviation.current.innerHTML = `${diff > 0 ? '+' : ''}${diff.toFixed(2)}%`;
}, [data]);
return (
<React.Fragment>
<div className="px-5 py-3">
<div className="flex items-start">
<div className="text-3xl font-bold text-slate-800 mr-2 tabular-nums">$<span ref={chartValue}>57.81</span></div>
<div ref={chartDeviation} className="text-sm font-semibold text-white px-1.5 rounded-full"></div>
</div>
</div>
<div className="grow">
<canvas ref={canvas} data={data} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default RealtimeChart;