viewof plot = {
const outerPadding = 20;
const categoryHeight = 400;
const categoryWidth = 200;
const valueSize = 30;
const totalHeight =
categoryHeight * data.categories.length + outerPadding * 2;
const totalWidth = categoryWidth + outerPadding;
let svg = d3
.create("svg")
.attr("width", totalWidth)
.attr("height", totalHeight);
let categoryBoxes = svg
.selectAll("g")
.data(data.categories)
.enter()
.append("g")
.attr(
"transform",
(d, i) => `translate(0, ${i * categoryHeight + outerPadding})`
);
categoryBoxes
.append("rect")
.attr("x", outerPadding)
.attr("y", 0)
.attr("width", categoryWidth)
.attr("height", categoryHeight)
.attr("fill", "white")
.attr("stroke", "black");
let valueScale = d3
.scaleLinear()
.domain([
0,
d3.max(data.categories, (d) => d3.max(d.values, (d) => d.value))
])
.range(["white", "red"]);
let valueBoxes = categoryBoxes
.selectAll("rect")
.data((d) => d.values)
.enter()
.append("rect")
.attr("x", (d, i) => outerPadding + (i % 3) * valueSize)
.attr("y", (d, i) => Math.floor(i / 3) * valueSize)
.attr("width", valueSize)
.attr("height", valueSize)
.attr("fill", (d) => valueScale(d.value))
.attr("stroke", "red")
.attr("class", (d, i) => `value-${i}`);
return svg.node();
}