function unitChart(
data,
{
group,
value,
width = 640,
height = 400,
margin = 10,
marginTop,
marginRight,
marginLeft,
marginBottom,
scheme = schemeCategory10,
formatLabel = (d) => `${accessor(group)(d)} (${d.__ratio.toFixed(1)}%)`,
cellPadding = 0.0333,
mark = "square",
showLegend = true,
legendCellSize = width / 32,
text = (d) => d,
fontSize,
src = function () {
throw "src not not passed in the options";
}
} = {}
) {
const getValue = accessor(value);
const getGroup = accessor(group);
const w = width;
const h = height ?? (showLegend ? (w * 2) / 3 : w);
marginTop = marginTop ?? margin;
marginRight = marginRight ?? margin;
marginBottom = marginBottom ?? margin;
marginLeft = marginLeft ?? margin;
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, w, h])
.attr("width", w)
.attr("height", h)
.style("display", "block")
.style("background", "white")
.style("height", "auto")
.style("height", "intrinsic")
.style("max-width", "100%");
if (height) {
svg.style("width", w).style("height", h);
}
const boundedWidth = w - marginLeft - marginRight;
const boundedHeight = h - marginTop - marginBottom;
const plotSize = boundedWidth < boundedHeight ? boundedWidth : boundedHeight;
const scale = d3
.scaleBand()
.domain(d3.range(10))
.range([0, plotSize])
.paddingInner(cellPadding);
const color = d3.scaleOrdinal(scheme).domain(d3.range(data.length));
let dataCopy = [...data].sort((a, b) => getValue(b) - getValue(a));
const total = computeTotal(dataCopy, getValue);
dataCopy = computeRatio(dataCopy, getValue, total);
const plotData = computePlotData(dataCopy, getGroup);
const groups = [...new Set(plotData.map((d) => d.group))];
const cellSize = scale.bandwidth();
const halfCellSize = cellSize / 2;
const plot = svg
.append("g")
.attr("transform", `translate(${marginLeft},${marginTop})`);
const cells = plot
.selectAll(".cell")
.data(plotData)
.join("g")
.attr("fill", (d) => (d.index === -1 ? "#ddd" : color(d.index)));
if (mark === "circle") {
cells
.append("circle")
.attr("cx", (d) => scale(d.x) + halfCellSize)
.attr("cy", (d) => scale(d.y) + halfCellSize)
.attr("r", halfCellSize);
} else if (mark === "text") {
cells
.append("text")
.text((c) => text(c.group))
.attr("x", (d) => scale(d.x) + halfCellSize)
.attr("y", (d) => scale(d.y) + halfCellSize)
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.attr("font-size", `${fontSize || cellSize * 0.5}px`);
} else if (mark === "image") {
cells
.append("image")
.attr("x", (d) => scale(d.x))
.attr("y", (d) => scale(d.y))
.attr("width", cellSize)
.attr("height", cellSize)
.attr("preserveAspectRatio", "xMinYMin")
.attr("href", (d) => src(d.group));
} else {
cells
.append("rect")
.attr("x", (d) => scale(d.x))
.attr("y", (d) => scale(d.y))
.attr("width", cellSize)
.attr("height", cellSize);
}
if (showLegend) {
const legend = svg
.append("g")
.attr(
"transform",
(d, i) => `translate(${marginLeft + cellSize + plotSize},${marginTop})`
)
.selectAll(".legend")
.data(dataCopy.filter((d) => groups.includes(getGroup(d))))
.join("g")
.attr("class", "legend")
.attr("transform", (d, i) => `translate(0,${i * legendCellSize * 1.5})`);
if (mark === "text") {
legend
.append("text")
.text((d) => text(getGroup(d)))
.attr("x", legendCellSize / 2) // 2
.attr("y", legendCellSize / 2) // 2
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.attr("font-size", `${legendCellSize * 1.1}px`); // this controls the icon size; let's make it larger - and make corresponding text larger
} else if (mark === "image") {
legend
.append("image")
.attr("width", legendCellSize)
.attr("height", legendCellSize)
.attr("preserveAspectRatio", "xMinYMin")
.attr("href", (d) => src(getGroup(d)));
} else {
legend
.append("rect")
.attr("width", legendCellSize)
.attr("height", legendCellSize)
.attr("rx", (d) => (mark === "circle" ? legendCellSize : 0))
.attr("fill", (d, i) => color(i));
}
legend
.append("text")
.attr("x", legendCellSize * 1.5) //1.25
.attr("y", legendCellSize * 0.5) //0.5
.attr("dominant-baseline", "middle")
.text(formatLabel)
.style("font-family", fontFamily)
.style("font-size", `${legendCellSize * 0.8}px`);
}
return svg.node();
}