이번에는 차트에 click event를 넣어, 클릭하는 index 총합의 데이터를 하단 영역에 보이도록 만들어 볼 것이다.
이전 포스팅에 이어 믹스형 차트에서 진행할 것이기에, 처음부터 보고싶은 사람들은 이전 포스팅을 참고하시길 바란다 🙂
먼저, ChartJS 에서 안내하는 이벤트 사용법은 다음과 같다.
Interactions | Chart.js
Interactions Namespace: options.interaction, the global interaction configuration is at Chart.defaults.interaction. To configure which events trigger chart interactions, see events. Name Type Default Description mode string 'nearest' Sets which elements ap
www.chartjs.org
문서에 더 자세히 나와있지만, 설명을 해석해 보자면 options.event에 사용할 event를 등록해 주고, 관련 이벤트 함수를 적용해 주면 된다!
그러나 나의 경우, react-chartjs-2 를 함께 사용하고 있기 때문에, 해당 문서에서 제안하는 event를 사용해 보았다.
click event 참고 문서 - react-chartjs-2
react-chartjs-2를 참고해서 쓰기가 훨씬 간단하다~ react-chartjs-2에서는 다음과 같이 3가지의 이벤트를 제공해주고 있는데 어떤 차이가 있는지 함께 알아보자!
👻 1) getDatasetAtEvent
const chartRef = useRef();
const onClick = (
event: React.MouseEvent<HTMLCanvasElement, globalThis.MouseEvent>
) => {
const chart = chartRef.current;
if (!chart) return;
console.log(getDatasetAtEvent(chart, event));
};
return (
<div>
<Chart
ref={chartRef}
type="bar"
data={mixData}
options={mixOptions}
onClick={onClick}
/>
</div>
);
먼저 getDatasetAtEvent는 클릭한 차트 타입의 모든 element에 대한 정보들을 출력해 준다.
예를 들어 barElement를 클릭했을 경우, 다음처럼 12개의 데이터가 콘솔로 출력되었다.
👻 2) getElementAtEvent
상단 console.log 에서 getElementAtEvent 로만 바꿔주면, 변경된 출력 내용을 확인해 볼 수 있다.
getElementAtEvent는 클릭한 차트타입 한 개만 콘솔에 찍어준다.
barElement의 특정 index를 클릭해 주면 다음과 같이 콘솔이 출력된다.
👻 3) getElementsAtEvent
getElementsAtEvent는 클릭한 index에 있는 모든 chart type에 대한 정보를 콘솔로 출력해 준다.
다음과 같이 bar 형, line 형 차트 3가지를 모두 콘솔에 찍어주는 것을 알 수 있다.
상황에 따라 위 3가지 중 한 가지를 선택하면 될 것이다!
나의 경우, 3가지의 index를 모두 출력하게 해야 하기 때문에 getElementsAtEvent를 활용해 볼 것이다.
그럼, 이제 해당 함수를 이용하여 클릭한 요소들의 값과 정보를 테이블로 보여주게 만드는 코드를 작성해 보자.
type TClickType = { label: string; cnt: number };
type TKeys = "total" | "react" | "vue";
//전체,react,vue를 담아 줄 state 만들어주기
const [clickType, setClickType] = useState<Record<TKeys, TClickType>>();
// 클릭하면 setClickType에 getElementAtEvent로부터 받아온 클릭한 index의 data 와 label 담아주기
const onClick = (
event: React.MouseEvent<HTMLCanvasElement, globalThis.MouseEvent>
) => {
const chart = chartRef.current;
if (!chart) return;
const click_result = getElementAtEvent(chart, event);
const dataIndex = click_result[0].index;
const label = labels_dummy[click_result[0].index];
setClickType({
total: { label, cnt: total_dummy[dataIndex] },
react: { label, cnt: react_dummy[dataIndex] },
vue: { label, cnt: vue_dummy[dataIndex] },
});
};
// click event 적용하기
<Chart
ref={chartRef}
type="bar"
data={mixData}
options={mixOptions}
onClick={onClick}
/>
다음과 같이 화면 하단에 표시가 된다 ⭐️
전체 코드는 다음과 같다
import { Chart, getElementAtEvent } from "react-chartjs-2";
import {
ArcElement,
BarElement,
CategoryScale,
Chart as ChartJS,
Legend,
LineElement,
LinearScale,
PointElement,
Tooltip,
registerables,
} from "chart.js";
import { useMemo, useRef, useState } from "react";
import styled from "styled-components";
ChartJS.register(
...registerables,
LinearScale,
CategoryScale,
BarElement,
PointElement,
LineElement,
Legend,
Tooltip,
ArcElement
);
type TClickType = { label: string; cnt: number };
type TKeys = "total" | "react" | "vue";
const MixChartExample = () => {
const [clickType, setClickType] = useState<Record<TKeys, TClickType>>();
const react_dummy = [10, 10, 10, 10, 20, 20, 20, 20, 30, 30, 30, 30];
// const react_flex_dummy = [
// 10, 10, 1000, 10, 20, 200, 20, 20, 330, 30, 1130, 30,
// ];
const vue_dummy = [10, 30, 10, 10, 10, 20, 30, 30, 20, 20, 20, 30];
const total_dummy = react_dummy.map((item, idx) => item + vue_dummy[idx]);
const labels_dummy = new Array(12).fill(0).map((_, idx) => idx + 1 + "월");
// 최대,최소값을 구하는
const min = useMemo(() => {
return Math.min.apply(null, total_dummy);
}, [total_dummy]);
const max = useMemo(() => {
return Math.max.apply(null, total_dummy);
}, [total_dummy]);
const mixOptions = {
scales: {
x: {
ticks: {
maxTicksLimit: 6,
},
},
y: {
ticks: {
color: "#808080",
stepSize: getStepSize(min, max),
},
},
},
};
//chart data
const mixData = {
labels: labels_dummy,
datasets: [
{
type: "line" as const,
label: "React" as string,
data: react_dummy,
borderColor: "#DD5353",
backgroundColor: "#DD5353",
borderWidth: 2, // 선의 크기
pointBorderWidth: 1, // point의 크기
tension: 0.1, // line 차트일 경우 선의 휘어짐 정도
},
{
type: "line" as const,
label: "Vue" as string,
data: vue_dummy,
borderColor: "#5F9DF7",
backgroundColor: "#5F9DF7",
borderWidth: 2,
pointBorderWidth: 1,
tension: 0.1, // line 차트일 경우 선의 휘어짐 정도
},
{
type: "bar" as const,
label: "전체" as string,
backgroundColor: "#ddd",
data: total_dummy,
borderColor: "#ddd",
borderWidth: 2,
},
],
};
const chartRef = useRef();
const onClick = (
event: React.MouseEvent<HTMLCanvasElement, globalThis.MouseEvent>
) => {
const chart = chartRef.current;
if (!chart) return;
const click_result = getElementAtEvent(chart, event);
const dataIndex = click_result[0].index;
const label = labels_dummy[click_result[0].index];
setClickType({
total: { label, cnt: total_dummy[dataIndex] },
react: { label, cnt: react_dummy[dataIndex] },
vue: { label, cnt: vue_dummy[dataIndex] },
});
};
return (
<div>
<Chart
ref={chartRef}
type="bar"
data={mixData}
options={mixOptions}
onClick={onClick}
/>
{clickType && (
<TableStyle>
<caption>선택한 index의 데이터</caption>
<tr>
<th>월</th>
<th>값</th>
</tr>
<tr>
<td rowSpan={3}>{clickType.total.label}</td>
<td>{clickType.total.cnt}</td>
</tr>
<tr>
<td>{clickType.react.cnt} 건</td>
</tr>
<tr>
<td>{clickType.vue.cnt}</td>
</tr>
</TableStyle>
)}
</div>
);
};
export default MixChartExample;
const TableStyle = styled.table`
width: 500px;
caption {
margin: 10px 0;
padding: 10px 0;
background-color: #eceaea;
}
th {
height: 40px;
background: #ddd;
}
td {
height: 25px;
border-bottom: 1px solid #ddd;
text-align: center;
}
`;
const getStepSize = (min: number, max: number) => {
const range = max - min; // 최댓값과 최솟값 간의 범위를 계산
const interval = Math.ceil(range / 3); // 범위를 3등분하여 각 구간의 크기를 계산
const exponent = Math.floor(Math.log10(interval)); // 각 구간의 크기에 대한 로그 값을 계산
const factor = Math.pow(10, exponent); // 10의 거듭제곱을 통해 가장 가까운 10의 배수로 구간 크기를 정규화
return Math.ceil(interval / factor) * factor;
};
끝!
'FE' 카테고리의 다른 글
[ReactFlow] 다이어그램 툴 만들기 #1 (0) | 2024.10.30 |
---|---|
[ReactFlow] 다이어그램 툴 만들기 #0 (3) | 2024.10.30 |
[ChartJS / react-chartjs-2] mix형(bar/line) 차트 만들어보기 #4 (2) | 2024.10.29 |
[ChartJS / react-chartjs-2] options 를 알아보기 #3 (0) | 2024.10.29 |
[ChartJS / react-chartjs-2] 완벽한 반응형 차트를 구현해보자 #2 (0) | 2024.10.29 |