diff --git a/charts.ts b/charts.ts
new file mode 100755
index 0000000..762839f
--- /dev/null
+++ b/charts.ts
@@ -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";
diff --git a/comps/charts/area.tsx b/comps/charts/area.tsx
new file mode 100755
index 0000000..e45d6ed
--- /dev/null
+++ b/comps/charts/area.tsx
@@ -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 (
+ <>
+
+ >
+ );
+};
diff --git a/comps/charts/bar.tsx b/comps/charts/bar.tsx
new file mode 100755
index 0000000..6ee5ad2
--- /dev/null
+++ b/comps/charts/bar.tsx
@@ -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 (
+ <>
+
+ >
+ );
+};
diff --git a/comps/charts/doughnut.tsx b/comps/charts/doughnut.tsx
new file mode 100755
index 0000000..e3a1ce2
--- /dev/null
+++ b/comps/charts/doughnut.tsx
@@ -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 (
+ <>
+
+ >
+ );
+};
+
+// [
+// {
+// 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,
+// },
+// ]
diff --git a/comps/charts/line.tsx b/comps/charts/line.tsx
new file mode 100755
index 0000000..c8f768c
--- /dev/null
+++ b/comps/charts/line.tsx
@@ -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 (
+ <>
+
+ >
+ );
+};
+
+// [
+// {
+// 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)',
+// },
+// ]
diff --git a/comps/charts/loader.ts b/comps/charts/loader.ts
new file mode 100755
index 0000000..323b9bf
--- /dev/null
+++ b/comps/charts/loader.ts
@@ -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 & { _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;
+};
diff --git a/comps/charts/pie.tsx b/comps/charts/pie.tsx
new file mode 100755
index 0000000..321bf30
--- /dev/null
+++ b/comps/charts/pie.tsx
@@ -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 (
+ <>
+
+ >
+ );
+};
+
+// [
+// {
+// 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,
+// },
+// ]
diff --git a/comps/charts/polar.tsx b/comps/charts/polar.tsx
new file mode 100755
index 0000000..0152e70
--- /dev/null
+++ b/comps/charts/polar.tsx
@@ -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 (
+ <>
+ {
+ 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,
+// },
+// ]
\ No newline at end of file