horizontalBarChart= (data, options)=> {
options = options || {}
const axisInc = options["axisInc"] || 10
const spacing = options["spacing"] || 6
const title = options["title"] || ""
const description = options["description"] || ""
const lineHeight = 8
const xAxisTickFormatFn = options["xAxisTickFormatFn"] || ((x)=>(x))
const betweenSectionPadding = 0.1
const barFn = options["barFn"] || ((data, params) => {
const {x, y, margin} = params;
return data.map( (d,i) => (
svg`
<rect
fill="#A3A3A3"
x="${x(0)}"
y="${y(d.names[1])}"
height="${y.bandwidth() - margin.spacing}"
width="${x(d.value) - x(0)}"/>`))})
const margin = ({top: 30, right: 20, bottom: 0, left: 110, spacing: spacing})
const width = 320;
const height = 3*spacing * data.length;
const xMax = Math.ceil(d3.max(data, d => d.value) / 10.0) * 10;
const x = d3.scaleLinear()
.domain([0, xMax])
.range([margin.left, width - margin.right]);
const axisTop= d3.axisTop(x).tickFormat( xAxisTickFormatFn )
const xAxis = (g) => (
g
.attr("transform", `translate(0,${margin.top-margin.spacing})`)
.call(axisTop.tickValues(d3.range(0, xMax+1, axisInc)))
.call(axisTop.tickSizeOuter(0).tickSize(4))
.call(g => (g.selection ? g.selection() : g).select(".domain").remove()))
const y0Values = Array.from(new Set(data.map(d => d.names[0])))
const y0 = d3.scaleBand()
.paddingInner(betweenSectionPadding)
.domain(y0Values)
.range([margin.top, height-margin.bottom])
// .align(0);
const axisLeft1 = d3.axisLeft(y0)
const y0Axis = (g) => g
.attr("transform", `translate(${margin.spacing},
${-y0.step()*0.5 + (y0.step()*betweenSectionPadding*0.5) + margin.spacing*0.7 })`)
.call(axisLeft1.tickSize(0))
.call(g => (g.selection ? g.selection() : g).selectAll("text").style("text-anchor", "start"))
.call(g => (g.selection ? g.selection() : g).select(".domain").remove())
const background = y0Values.map( (n,i) => {
if (i!==0) {
return svg`
<line
fill="#F6F6F600"
stroke="#E3E3E3"
x1="${-2}"
y1="${y0(n)-(y0.step()*betweenSectionPadding + margin.spacing)*0.5}"
x2="${width+4}"
y2="${y0(n)-(y0.step()*betweenSectionPadding + margin.spacing)*0.5}"
/>`}})
const rollup= d3.nest()
.key(d=>d.names[0])
.object(data)
const sections = Object.keys(rollup).map(name0=>{
const yValues = Array.from(new Set(rollup[name0].map(d => d.names[1])))
const y = d3.scaleBand()
// .padding(0).paddingOuter(margin.spacing / y0.bandwidth())
.domain(yValues)
.range([0, y0.bandwidth()]);
// return y0.bandwidth()
const axisLeft = d3.axisLeft(y).tickFormat((x)=>x)
const yAxis = (g) => (
g
.attr("transform", `translate(${margin.left-margin.spacing*0.5},
${-margin.spacing*0.5})`)
.call(axisLeft.tickSizeOuter(0).tickSize(0))
.call(g => (g.selection ? g.selection() : g).select(".domain").remove())
)
return svg`
<g transform="translate(${0}, ${y0(name0)})">
${barFn(rollup[name0], {x, y, margin, width})}
${d3.select(svg`<g>`).call(yAxis).node()}
</g>`})
// return sections
return html`
<style>
.tick line {
stroke-width: 1;
stroke: #A3A3A3;
}
</style>
<div style="background-color: #FCFCFC; max-width: ${width}px;">
<h2>${title}</h2>
<span>${description}</span>
<br/>
<svg viewBox="0 0 ${width} ${height}" style="max-width: ${width}px;">
<g class="bars">
${background}
${sections}
</g>
${d3.select(svg`<g>`).call(xAxis).node()}
${d3.select(svg`<g>`).call(y0Axis).node()}
</svg>
</div>`
}