Files
skills-framework-demonstrator/src/components/QuestionGroupChart.tsx
2025-07-10 08:33:42 +02:00

99 lines
2.7 KiB
TypeScript

import MaterialIcon from "@mui/material/Icon";
import * as d3 from "d3";
import { useEffect, useRef } from "react";
import { colorScheme, config } from "../config";
import { CategoryMetadata } from "../lib/metadata";
import "./Chart.css";
interface QuestionGroupChartProps {
question: string;
metadata?: CategoryMetadata;
groupData: { response: string }[];
responses: string[];
xScale: d3.ScaleBand<string>;
}
function makeDot(
g: d3.Selection<SVGGElement, unknown, null, undefined>,
dotX: number,
dotY: number
) {
const shape = config.dotShape;
if (shape === "circle") {
return g
.append("circle")
.attr("class", "dot")
.attr("cx", dotX)
.attr("cy", dotY)
.attr("r", config.dotRadius);
} else if (shape === "rect") {
return g
.append("rect")
.attr("class", "dot")
.attr("x", dotX - config.dotRadius)
.attr("y", dotY - config.dotRadius)
.attr("width", config.dotRadius * 2)
.attr("height", config.dotRadius * 2);
}
throw new Error(`Unsupported shape: ${shape}`);
}
export function QuestionGroupChart({
question,
metadata,
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 chartHeight = config.chartHeight;
const chartWidth = xScale.range()[1];
const svg = d3
.select(svgRef.current)
.attr("width", chartWidth)
.attr("height", chartHeight);
const g = svg.append("g");
// 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 - (row + 1) * (config.dotRadius * 2 + config.dotSpacing);
makeDot(g, dotX, dotY).attr("fill", colorScheme[response] || "#666");
});
});
}, [groupData, responses, xScale, question]);
return (
<div className="question-group">
<svg ref={svgRef}></svg>
<div className="question-title">
{metadata?.icon && <MaterialIcon>{metadata.icon}</MaterialIcon>}
{question}
</div>
<p>{metadata?.description || question}</p>
</div>
);
}
export default QuestionGroupChart;