add charts

This commit is contained in:
rizky 2024-04-25 02:50:59 -07:00
parent e09ca57c61
commit bf2745b4e1
8 changed files with 570 additions and 0 deletions

6
charts.ts Executable file
View File

@ -0,0 +1,6 @@
export { AreaChart } from "@/comps/charts/area";
export { BarChart } from "@/comps/charts/bar";
export { DoughnutChart } from "@/comps/charts/doughnut";
export { LineChart } from "@/comps/charts/line";
export { PieChart } from "@/comps/charts/pie";
export { PolarAreaChart } from "@/comps/charts/polar";

61
comps/charts/area.tsx Executable file
View File

@ -0,0 +1,61 @@
import { useLocal } from "@/utils/use-local";
import { FC, useEffect } from "react";
import { loadChart } from "./loader";
import type { Line } from "react-chartjs-2";
import type { ChartData } from "chart.js";
type ItemData = ChartData<"line", number[], unknown>;
const getRandomInt = (min: number, max: number) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
export const AreaChart: FC<{
data: () => ItemData;
legend?: any;
}> = ({ data, legend }) => {
const local = useLocal({
data: null as unknown as ItemData,
Line: null as null | typeof Line,
});
useEffect(() => {
local.data = data();
local.render();
}, [data]);
// lazy load: Line Chart
if (!local.Line) {
loadChart().then((chart) => {
local.Line = chart.Line;
local.render();
});
return <>Loading...</>;
}
return (
<>
<local.Line
datasetIdKey="id"
options={{
responsive: true,
plugins:
legend === "none"
? {
legend: {
display: false,
},
}
: {
legend: {
position: legend as any,
},
},
}}
data={local.data}
/>
</>
);
};

61
comps/charts/bar.tsx Executable file
View File

@ -0,0 +1,61 @@
import { useLocal } from "@/utils/use-local";
import { FC, useEffect } from "react";
import { loadChart } from "./loader";
import type { Bar } from "react-chartjs-2";
import type { ChartData } from "chart.js";
type ItemData = ChartData<"bar", number[], unknown>;
const getRandomInt = (min: number, max: number) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
export const BarChart: FC<{
data: () => ItemData;
legend?: any
}> = ({ data, legend }) => {
const local = useLocal({
data: null as unknown as ItemData,
Bar: null as null | typeof Bar,
});
useEffect(() => {
local.data = data();
local.render();
}, [data]);
// lazy load: Line Chart
if (!local.Bar) {
loadChart().then((chart) => {
local.Bar = chart.Bar;
local.render();
});
return <>Loading...</>;
}
return (
<>
<local.Bar
datasetIdKey="id"
options={{
responsive: true,
plugins:
legend === "none"
? {
legend: {
display: false,
},
}
: {
legend: {
position: legend as any,
},
},
}}
data={local.data}
/>
</>
);
};

87
comps/charts/doughnut.tsx Executable file
View File

@ -0,0 +1,87 @@
import { useLocal } from "@/utils/use-local";
import { FC, useEffect } from "react";
import { loadChart } from "./loader";
import type { Doughnut } from "react-chartjs-2";
import type { ChartData } from "chart.js";
type ItemData = ChartData<"doughnut", number[], unknown>;
const getRandomInt = (min: number, max: number) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
export const DoughnutChart: FC<{
data: () => ItemData;
legend?: any;
}> = ({ data, legend }) => {
const local = useLocal({
data: null as unknown as ItemData,
Doughnut: null as null | typeof Doughnut,
});
useEffect(() => {
local.data = data();
local.render();
}, [data]);
// lazy load: Line Chart
if (!local.Doughnut) {
loadChart().then((chart) => {
local.Doughnut = chart.Doughnut;
local.render();
});
return <>Loading...</>;
}
// const labels = ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"];
return (
<>
<local.Doughnut
datasetIdKey="id"
options={{
responsive: true,
plugins:
legend === "none"
? {
legend: {
display: false,
},
}
: {
legend: {
position: legend as any,
},
},
}}
data={local.data}
/>
</>
);
};
// [
// {
// label: "# of Votes",
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: [
// "rgba(255, 99, 132, 0.2)",
// "rgba(54, 162, 235, 0.2)",
// "rgba(255, 206, 86, 0.2)",
// "rgba(75, 192, 192, 0.2)",
// "rgba(153, 102, 255, 0.2)",
// "rgba(255, 159, 64, 0.2)",
// ],
// borderColor: [
// "rgba(255, 99, 132, 1)",
// "rgba(54, 162, 235, 1)",
// "rgba(255, 206, 86, 1)",
// "rgba(75, 192, 192, 1)",
// "rgba(153, 102, 255, 1)",
// "rgba(255, 159, 64, 1)",
// ],
// borderWidth: 1,
// },
// ]

76
comps/charts/line.tsx Executable file
View File

@ -0,0 +1,76 @@
import { useLocal } from "@/utils/use-local";
import { FC, useEffect } from "react";
import { loadChart } from "./loader";
import type { Line } from "react-chartjs-2";
import type { ChartData } from "chart.js";
type ItemData = ChartData<"line", number[], unknown>;
const getRandomInt = (min: number, max: number) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
export const LineChart: FC<{
data: () => ItemData;
legend?: any;
}> = ({ data, legend }) => {
const local = useLocal({
data: null as unknown as ItemData,
Line: null as null | typeof Line,
});
useEffect(() => {
local.data = data();
local.render();
}, [data]);
// lazy load: Line Chart
if (!local.Line) {
loadChart().then((chart) => {
local.Line = chart.Line;
local.render();
});
return <>Loading...</>;
}
return (
<>
<local.Line
datasetIdKey="id"
options={{
responsive: true,
plugins:
legend === "none"
? {
legend: {
display: false,
},
}
: {
legend: {
position: legend as any,
},
},
}}
data={local.data}
/>
</>
);
};
// [
// {
// label: 'Dataset 1',
// data: labels.map(() => getRandomInt(10, 100)),
// borderColor: 'rgb(255, 99, 132)',
// backgroundColor: 'rgba(255, 99, 132, 0.5)',
// },
// {
// label: 'Dataset 2',
// data: labels.map(() => getRandomInt(10, 100)),
// borderColor: 'rgb(53, 162, 235)',
// backgroundColor: 'rgba(53, 162, 235, 0.5)',
// },
// ]

87
comps/charts/loader.ts Executable file
View File

@ -0,0 +1,87 @@
import { lazy } from "react";
import type { Bar, Line, Pie, Doughnut, PolarArea } from "react-chartjs-2";
export type Loader = {
Line: typeof Line;
Bar: typeof Bar;
Pie: typeof Pie;
Doughnut: typeof Doughnut;
PolarArea: typeof PolarArea;
};
export const w = window as unknown as {
chartjs_loaded?: Partial<Loader> & { _import: any };
};
export const loadChart = async () => {
if (!w.chartjs_loaded) {
w.chartjs_loaded = { _import: null };
}
const load = async () => {
if (w.chartjs_loaded) {
if (!w.chartjs_loaded._import) {
const {
CategoryScale,
Chart: ChartJS,
Legend,
LineElement,
LinearScale,
PointElement,
Title,
Tooltip,
Filler,
BarElement,
ArcElement,
RadialLinearScale,
} = await import("chart.js");
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
Filler,
BarElement,
ArcElement,
RadialLinearScale
);
const { Line } = await import("react-chartjs-2");
const { Bar } = await import("react-chartjs-2");
const { Pie } = await import("react-chartjs-2");
const { Doughnut } = await import("react-chartjs-2");
const { PolarArea } = await import("react-chartjs-2");
w.chartjs_loaded.Line = Line;
w.chartjs_loaded.Bar = Bar;
w.chartjs_loaded.Pie = Pie;
w.chartjs_loaded.Doughnut = Doughnut;
w.chartjs_loaded.PolarArea = PolarArea;
w.chartjs_loaded._import = true;
}
}
return w.chartjs_loaded as Loader;
};
const c = await load();
return {
Line: lazy(async () => {
return { default: c.Line };
}),
Bar: lazy(async () => {
return { default: c.Bar };
}),
Pie: lazy(async () => {
return { default: c.Pie };
}),
Doughnut: lazy(async () => {
return { default: c.Doughnut };
}),
PolarArea: lazy(async () => {
return { default: c.PolarArea };
}),
} as Loader;
};

85
comps/charts/pie.tsx Executable file
View File

@ -0,0 +1,85 @@
import { useLocal } from "@/utils/use-local";
import { FC, useEffect } from "react";
import { loadChart } from "./loader";
import type { Pie } from "react-chartjs-2";
import type { ChartData } from "chart.js";
type ItemData = ChartData<"pie", number[], unknown>;
const getRandomInt = (min: number, max: number) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
export const PieChart: FC<{
data: () => ItemData;
legend?: any;
}> = ({ data, legend }) => {
const local = useLocal({
data: null as unknown as ItemData,
Pie: null as null | typeof Pie,
});
useEffect(() => {
local.data = data();
local.render();
}, [data]);
// lazy load: Line Chart
if (!local.Pie) {
loadChart().then((chart) => {
local.Pie = chart.Pie;
local.render();
});
return <>Loading...</>;
}
return (
<>
<local.Pie
datasetIdKey="id"
options={{
responsive: true,
plugins:
legend === "none"
? {
legend: {
display: false,
},
}
: {
legend: {
position: legend as any,
},
},
}}
data={local.data}
/>
</>
);
};
// [
// {
// label: "# of Votes",
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: [
// "rgba(255, 99, 132, 0.2)",
// "rgba(54, 162, 235, 0.2)",
// "rgba(255, 206, 86, 0.2)",
// "rgba(75, 192, 192, 0.2)",
// "rgba(153, 102, 255, 0.2)",
// "rgba(255, 159, 64, 0.2)",
// ],
// borderColor: [
// "rgba(255, 99, 132, 1)",
// "rgba(54, 162, 235, 1)",
// "rgba(255, 206, 86, 1)",
// "rgba(75, 192, 192, 1)",
// "rgba(153, 102, 255, 1)",
// "rgba(255, 159, 64, 1)",
// ],
// borderWidth: 1,
// },
// ]

107
comps/charts/polar.tsx Executable file
View File

@ -0,0 +1,107 @@
import { useLocal } from "@/utils/use-local";
import { FC, useEffect } from "react";
import { loadChart } from "./loader";
import type { PolarArea } from "react-chartjs-2";
type ItemData = {
label: string;
data: number[];
backgroundColor: string[];
borderColor: string[];
borderWidth: number;
}; // { }
type ItemLabel = string;
type TitleType = string;
const getRandomInt = (min: number, max: number) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
export const PolarAreaChart: FC<{
data: () => ItemData[];
labels: ItemLabel[];
title: TitleType;
}> = ({ data, labels, title }) => {
const local = useLocal({
data: [] as ItemData[],
labels: [] as ItemLabel[],
title: '' as TitleType,
PolarArea: null as null | typeof PolarArea,
});
useEffect(() => {
local.data = data();
local.labels = labels;
local.title = title;
local.render();
}, [data]);
// lazy load: Line Chart
if (!local.PolarArea) {
loadChart().then((chart) => {
local.PolarArea = chart.PolarArea;
local.render();
});
return <>Loading...</>;
}
// const labels = ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"];
return (
<>
<local.PolarArea
datasetIdKey="id"
options={{
responsive: true,
plugins: {
legend: {
position: "top" as const,
},
title: {
display: true,
text: local.title,
},
},
}}
data={{
labels: local.labels,
datasets: local.data.map((e) => {
return {
label: e.label,
data: e.data,
backgroundColor: e.backgroundColor,
borderColor: e.borderColor,
borderWidth: e.borderWidth
}
}),
}}
/>
</>
);
};
// [
// {
// label: "# of Votes",
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: [
// "rgba(255, 99, 132, 0.2)",
// "rgba(54, 162, 235, 0.2)",
// "rgba(255, 206, 86, 0.2)",
// "rgba(75, 192, 192, 0.2)",
// "rgba(153, 102, 255, 0.2)",
// "rgba(255, 159, 64, 0.2)",
// ],
// borderColor: [
// "rgba(255, 99, 132, 1)",
// "rgba(54, 162, 235, 1)",
// "rgba(255, 206, 86, 1)",
// "rgba(75, 192, 192, 1)",
// "rgba(153, 102, 255, 1)",
// "rgba(255, 159, 64, 1)",
// ],
// borderWidth: 1,
// },
// ]