function dotTileV3 (data, {
title = null,
w = 300,
radius = 12,
startAtZero = true,
xPadding = 20,
bgdCol = '#e4f1e6',
bgdList = '#f2f8f3',
listLabels = ['%', 'Demographic', 'Count'],
lineCol = '#688e70',
circleFill = '#f59994',
circleTextFill = bgdCol,
titleCol = '#394437',
titlePadding = 35,
offsetHeight = titlePadding + 140,
h = (data.length * 35) + offsetHeight
} = {}) {
const min = (startAtZero) ? 0 : d3.min(data, d => d.value);
const max = d3.max(data, d => d.value);
const dataDecending = _.sortBy(data, 'value')
.map((d, i) => {
const obj = d;
obj.id = `ref-${i}`;
return obj
})
const xScale = d3.scaleLinear()
.domain([min, max * 1.1])
.range([xPadding, w - (xPadding * 2)]);
const svg = DOM.svg(w, h);
const sel = d3.select(svg);
// background colour
sel.append('rect')
.attr('width', w)
.attr('height', h)
.attr('rx', 20)
.attr('fill', bgdCol);
// append the title
if (title !==null) {
sel.append('text')
.attr('y', titlePadding)
.attr('x', w/2)
.attr('text-anchor', 'middle')
.style('fill', titleCol)
.text(title);
}
// show labels if they exist
if (!_.isEmpty(listLabels)) {
// TODO remove some of the repetition encapsulate into a function or css style
sel.append('text')
.style('font-size', '11px')
.attr('text-anchor', 'end')
.attr('y', offsetHeight - 40)
.attr('x', xPadding + 35)
.style('fill', titleCol)
.text(listLabels[0]);
sel.append('text')
.style('font-size', '10px')
.attr('text-anchor', 'middle')
.attr('y', offsetHeight - 40)
.attr('x', w/2)
.style('text-transform', 'uppercase')
.style('letter-spacing', '2px')
.style('fill', titleCol)
.text(listLabels[1]);
sel.append('text')
.style('font-size', '10px')
.attr('y', offsetHeight - 40)
.attr('text-anchor', 'end')
.attr('x', w - xPadding)
.style('text-transform', 'uppercase')
.style('letter-spacing', '2px')
.style('fill', titleCol)
.text(listLabels[2]);
}
// have line for values
sel.append('line')
.attr('x1', xPadding)
.attr('y1', offsetHeight/2 -10)
.attr('x2', w - xPadding)
.attr('y2', offsetHeight/2 -10)
.attr('stroke', lineCol)
.attr('stroke-dasharray', 2);
// add circles
sel.selectAll('circle')
.data(dataDecending)
.join('circle')
.attr('class', d => `${d.id} item`)
.attr('fill', circleFill)
.attr('cx', d => xScale(d.value))
.attr('cy', offsetHeight/2 -10)
.attr('r', radius)
.attr('stroke', bgdCol)
.style('opacity', 0.9)
.on('mouseover', function (e, d) {
mouseover(e, d)
})
const list = sel.selectAll('g')
// pass in sorted data so can apply values one above and below (index order matches visual order)
.data(_.sortBy(data, d => d.value))
.join('g')
.attr('class', d => `${d.id} item`)
.attr('transform', (d, i) => {
return `translate(${0}, ${i * 35 + offsetHeight})`
});
list.append('rect')
.attr('width', w - (xPadding))
.attr('x', xPadding/2)
.attr('height', 30)
.attr('y', -20)
.attr('rx', 10)
.attr('fill', bgdList)
.style('cursor', 'pointer')
.on('mouseover', function (e, d) {
mouseover(e, d)
})
list.append('text')
.style('font-size', '13px')
.attr('text-anchor', 'end')
.attr('x', xPadding + 35)
.text(d => d.value);
list.append('text')
.style('font-size', '13px')
.attr('text-anchor', 'middle')
.attr('x', w/2)
.text(d => d.label);
list.append('text')
.style('font-size', '13px')
.attr('text-anchor', 'end')
.attr('x', w - xPadding)
.text(d => d.population);
function mouseover(e, d) {
sel.selectAll(`.item`).style('opacity', 0.4);
sel.selectAll(`.${d.id}`).style('opacity', 1);
}
return svg;
}