120 lines
2.9 KiB
TypeScript
120 lines
2.9 KiB
TypeScript
import { Chart } from "react-chartjs-2";
|
|
import {
|
|
Chart as ChartJS,
|
|
CategoryScale,
|
|
LinearScale,
|
|
BarController,
|
|
BarElement,
|
|
LineController,
|
|
LineElement,
|
|
PointElement,
|
|
Title,
|
|
Tooltip,
|
|
Legend,
|
|
} from "chart.js";
|
|
import type { ChartOptions } from "chart.js";
|
|
|
|
ChartJS.register(
|
|
CategoryScale,
|
|
LinearScale,
|
|
BarController,
|
|
BarElement,
|
|
LineController,
|
|
LineElement,
|
|
PointElement,
|
|
Title,
|
|
Tooltip,
|
|
Legend,
|
|
);
|
|
|
|
// 차트 설정 외부 정의 (Mixed 타입 명시)
|
|
const options: ChartOptions<"bar" | "line"> = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
x: { stacked: true },
|
|
y: {
|
|
stacked: true,
|
|
beginAtZero: true,
|
|
title: { display: true, text: "인구수(명)" },
|
|
},
|
|
y1: {
|
|
position: "right",
|
|
beginAtZero: true,
|
|
grid: { drawOnChartArea: false },
|
|
title: { display: true, text: "총 인구(명)" },
|
|
},
|
|
},
|
|
plugins: {
|
|
legend: { position: "bottom" },
|
|
},
|
|
};
|
|
|
|
export type PopulationChartData = {
|
|
X_AXIS: string[];
|
|
TOT: number[];
|
|
ORIGIN_MALE: (number | null)[];
|
|
ORIGIN_FEMALE: (number | null)[];
|
|
FOREIGN_MALE: (number | null)[];
|
|
FOREIGN_FEMALE: (number | null)[];
|
|
};
|
|
|
|
type Props = {
|
|
chartData: PopulationChartData | null;
|
|
};
|
|
|
|
const SERIES_META = [
|
|
{ key: "ORIGIN_MALE", name: "내국인 남성", color: "#3B82F6" },
|
|
{ key: "ORIGIN_FEMALE", name: "내국인 여성", color: "#EC4899" },
|
|
{ key: "FOREIGN_MALE", name: "외국인 남성", color: "#10B981" },
|
|
{ key: "FOREIGN_FEMALE", name: "외국인 여성", color: "#F59E0B" },
|
|
] as const;
|
|
|
|
const PopulationStatusChart = ({ chartData }: Props) => {
|
|
if (!chartData || chartData.X_AXIS.length === 0) {
|
|
return (
|
|
<div className="flex items-center justify-center h-full min-h-[40rem] w-full text-gray-400 text-[1.4rem] border border-gray-200 rounded">
|
|
조회된 데이터가 없습니다. 조회 버튼을 눌러주세요.
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const data = {
|
|
labels: chartData.X_AXIS.map((yr) => `${yr}년`),
|
|
datasets: [
|
|
...SERIES_META
|
|
.filter((s) => chartData[s.key]?.some((v) => v !== null))
|
|
.map((s) => ({
|
|
type: "bar" as const,
|
|
label: s.name,
|
|
data: chartData[s.key].map((v) => v ?? 0),
|
|
backgroundColor: s.color,
|
|
stack: "stack1",
|
|
})),
|
|
{
|
|
type: "line" as const,
|
|
label: "총 인구",
|
|
data: chartData.TOT,
|
|
borderColor: "#1F2937",
|
|
backgroundColor: "#1F2937",
|
|
borderDash: [5, 5],
|
|
tension: 0.1,
|
|
yAxisID: "y1",
|
|
},
|
|
],
|
|
};
|
|
|
|
return (
|
|
<div className="h-full w-full border border-gray-200 rounded p-4">
|
|
{/* 제네릭 타입을 통해 Mixed Chart 지원, 부모 컨테이너 크기에 맞춰 반응형으로 리사이즈됨 */}
|
|
<Chart<"bar" | "line", (string | number)[], string>
|
|
type="bar"
|
|
data={data}
|
|
options={options}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default PopulationStatusChart;
|