first commit
This commit is contained in:
162
src/charts/BarChart01.jsx
Normal file
162
src/charts/BarChart01.jsx
Normal 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
99
src/charts/BarChart02.jsx
Normal 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
156
src/charts/BarChart03.jsx
Normal 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
149
src/charts/BarChart04.jsx
Normal 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
157
src/charts/BarChart05.jsx
Normal 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
164
src/charts/BarChart06.jsx
Normal 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;
|
||||
42
src/charts/ChartjsConfig.jsx
Normal file
42
src/charts/ChartjsConfig.jsx
Normal 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();
|
||||
}
|
||||
},
|
||||
});
|
||||
114
src/charts/DoughnutChart.jsx
Normal file
114
src/charts/DoughnutChart.jsx
Normal 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;
|
||||
76
src/charts/LineChart01.jsx
Normal file
76
src/charts/LineChart01.jsx
Normal 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
153
src/charts/LineChart02.jsx
Normal 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;
|
||||
88
src/charts/LineChart03.jsx
Normal file
88
src/charts/LineChart03.jsx
Normal 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;
|
||||
78
src/charts/LineChart04.jsx
Normal file
78
src/charts/LineChart04.jsx
Normal 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
157
src/charts/LineChart05.jsx
Normal 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;
|
||||
94
src/charts/LineChart06.jsx
Normal file
94
src/charts/LineChart06.jsx
Normal 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;
|
||||
86
src/charts/LineChart07.jsx
Normal file
86
src/charts/LineChart07.jsx
Normal 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;
|
||||
84
src/charts/LineChart08.jsx
Normal file
84
src/charts/LineChart08.jsx
Normal 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;
|
||||
69
src/charts/LineChart09.jsx
Normal file
69
src/charts/LineChart09.jsx
Normal 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
117
src/charts/PieChart.jsx
Normal 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
113
src/charts/PolarChart.jsx
Normal 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;
|
||||
120
src/charts/RealtimeChart.jsx
Normal file
120
src/charts/RealtimeChart.jsx
Normal 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;
|
||||
Reference in New Issue
Block a user