function AreaChartWithDifferentFacetHeight(data, {
x = ([x]) => x,
y = ([, y]) => y,
z = () => 1,
defined,
curve = d3.curveLinear,
marginTop = 20,
marginRight = 0,
marginBottom = 20,
marginLeft = 0,
width = 640,
height = 640,
padding = 1,
xType = d3.scaleUtc,
xDomain,
xRange = [marginLeft, width - marginRight],
yType = d3.scaleLinear,
yDomain,
yRange,
zDomain,
zRange = d3.schemeCategory10,
cssclass = "area-chart",
strokeColor = "black",
} = {}){
const X = d3.map(data, x);
const Y = d3.map(data, y);
const Z = d3.map(data, z);
if (defined === undefined) defined = (d, i) => !isNaN(X[i]) && !isNaN(Y[i]);
const D = d3.map(data, defined);
const maxValuesMap = d3.rollup(data, v => d3.max(v, y), z);
const scaleMax = d3.scaleOrdinal([...maxValuesMap.keys()], [...maxValuesMap.values()]);
const totalMax = d3.sum(scaleMax.range());
const cumulative = scaleMax.range().map((sum => value => sum += value)(0));
// Compute default domains, and unique the z-domain.
if (xDomain === undefined) xDomain = d3.extent(X);
if (yDomain === undefined) yDomain = [0, totalMax];
if (zDomain === undefined) zDomain = new d3.InternSet(Z);
if (yRange === undefined) yRange = [height - marginBottom - padding * (zDomain.size - 1), 0];
// Omit any data not present in the z-domain.
const I = d3.range(X.length).filter(i => zDomain.has(Z[i]));
// Construct scales and axes.
const xScale = xType(xDomain, xRange);
const yScale = yType(yDomain, yRange);
const zScale = d3.scaleOrdinal(zDomain, zRange);
const y0Scale = d3.scaleOrdinal()
.domain(scaleMax.domain())
.range( cumulative.map(m => yScale(m)) );
const area = d3.area()
.defined(i => D[i])
.curve(curve)
.x(i => xScale(X[i]))
.y0(yScale(0))
.y1(i => yScale(Y[i]));
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("class", cssclass)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;")
.attr("font-family", "sans-serif")
.attr("font-size", 10);
const g = svg.selectAll("g")
.data(d3.group(I, i => Z[i]))
.join("g")
.attr("transform", (_, i) => `translate(0,${ padding * i - y0Scale(Z[i]) })`)
g.append("path")
.attr("d", ([, I]) => area(I))
.attr("fill", (_, i) => zScale(Z[i]))
.attr("stroke", strokeColor)
g.append("text")
.attr("x", marginLeft)
.attr("y", yScale(0))
.attr("dy", "-0.2em")
.style("font-size", "16px")
.style("stroke", "white")
.style("stroke-width", "3px")
.style("paint-order", "stroke")
.text(([z]) => z);
// Since there are normally no left or right margins, don’t show ticks that
// are close to the edge of the chart, as these ticks are likely to be clipped.
const xAxis = d3.axisBottom(xScale).ticks(width / 80).tickSizeOuter(0);
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(xAxis)
.style("font-size", "16px")
.call(g => g.selectAll(".tick")
.filter(d => xScale(d) < 10 || xScale(d) > width - 10)
.remove())
.call(g => g.select(".domain").remove());
return svg.node();
}