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