Migrate to React
This commit is contained in:
95
src/components/QuestionGroupChart.tsx
Normal file
95
src/components/QuestionGroupChart.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import * as d3 from "d3";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { colorScheme, config } from "../config";
|
||||
|
||||
interface QuestionGroupChartProps {
|
||||
question: string;
|
||||
groupData: { response: string }[];
|
||||
responses: string[];
|
||||
xScale: d3.ScaleBand<string>;
|
||||
}
|
||||
|
||||
export function QuestionGroupChart({
|
||||
question,
|
||||
groupData,
|
||||
responses,
|
||||
xScale,
|
||||
}: QuestionGroupChartProps) {
|
||||
const svgRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Clear SVG
|
||||
d3.select(svgRef.current).selectAll("*").remove();
|
||||
|
||||
// Group responses by category (within this group only)
|
||||
const responseGroups = d3.group(groupData, (d) => d.response);
|
||||
const maxCount =
|
||||
d3.max(Array.from(responseGroups.values(), (values) => values.length)) ||
|
||||
0;
|
||||
const maxRows = Math.ceil(maxCount / config.columnsPerGroup);
|
||||
|
||||
const chartHeight =
|
||||
maxRows * (config.dotRadius * 2 + config.dotSpacing) +
|
||||
config.margin.bottom;
|
||||
const chartWidth =
|
||||
xScale.range()[1] + config.margin.left + config.margin.right;
|
||||
|
||||
const svg = d3
|
||||
.select(svgRef.current)
|
||||
.attr("width", chartWidth)
|
||||
.attr("height", chartHeight);
|
||||
const g = svg
|
||||
.append("g")
|
||||
.attr(
|
||||
"transform",
|
||||
`translate(${config.margin.left},${config.margin.top})`
|
||||
);
|
||||
|
||||
// Dots
|
||||
responses.forEach((response) => {
|
||||
const responseData = responseGroups.get(response) || [];
|
||||
const x = xScale(response) || 0;
|
||||
responseData.forEach((_, index) => {
|
||||
const row = Math.floor(index / config.columnsPerGroup);
|
||||
const col = index % config.columnsPerGroup;
|
||||
const dotX =
|
||||
x +
|
||||
xScale.bandwidth() / 2 +
|
||||
(col - (config.columnsPerGroup - 1) / 2) *
|
||||
(config.dotRadius * 2 + config.dotSpacing);
|
||||
const dotY =
|
||||
chartHeight -
|
||||
config.margin.top -
|
||||
config.margin.bottom -
|
||||
(row + 1) * (config.dotRadius * 2 + config.dotSpacing);
|
||||
|
||||
g.append("circle")
|
||||
.attr("class", "dot")
|
||||
.attr("cx", dotX)
|
||||
.attr("cy", dotY)
|
||||
.attr("r", config.dotRadius)
|
||||
.attr("fill", colorScheme[response] || "#666");
|
||||
});
|
||||
});
|
||||
}, [groupData, responses, xScale, question]);
|
||||
|
||||
/*
|
||||
// Calculate height for container
|
||||
const responseGroups = d3.group(groupData, (d) => d.response);
|
||||
const maxCount =
|
||||
d3.max(Array.from(responseGroups.values(), (values) => values.length)) || 0;
|
||||
const maxRows = Math.ceil(maxCount / config.columnsPerGroup);
|
||||
const chartHeight =
|
||||
maxRows * (config.dotRadius * 2 + config.dotSpacing) +
|
||||
config.margin.top +
|
||||
config.margin.bottom;
|
||||
*/
|
||||
return (
|
||||
<div className="question-group">
|
||||
<div className="question-title">{question}</div>
|
||||
<svg ref={svgRef}></svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default QuestionGroupChart;
|
||||
Reference in New Issue
Block a user