Public
Edited
Aug 1, 2023
2 forks
3 stars
Insert cell
Insert cell
vis1 = html`<div class="vis-sb-1"></div>`
Insert cell
Insert cell
Insert cell
squareBar([
{
value: valuePct,
label: 'test',
targetDir: 'above', // target can be below/above i.e aim is to either be above or below the target
target: targetPct
}
], {
bind: d3.select(vis1), // pass in the selection
barWidth: 100,
valueFontSize: '24px'
})
Insert cell
Insert cell
vis2 = html`<div class="vis-sb-2"></div>`
Insert cell
data = [
{
value: 45,
label: 'T1',
targetDir: 'above', // target can be below/above i.e aim is to either be above or below the target
target: 40
},
{
value: 13,
label: 'T2',
targetDir: 'above',
target: 15
},
{
value: 20,
label: 'T3',
targetDir: 'above',
target: 15
},
{
value: 30,
label: 'T4',
targetDir: 'above',
target: 35
},
{
value: 15,
label: 'T5',
targetDir: 'above',
target: 15
}
]
Insert cell
squareBar(data, {
bind: d3.select(vis2), // pass in the selection
})
Insert cell
function squareBar (data, {
bind = null, // pass in a d3 selection i.e d3.select('class/div')
margin = { top: 30, right: 0, bottom: 10, left: 20 },
barWidth = 50,
barHeight = barWidth,
spacing = 2.2,
width = data.length * (barWidth * spacing),
height = barHeight + 20,
valueFormat = '.1f',
stroke = 'none',
strokeWidth = 0,
opacity = 1,
labelFontSize = '14px',
valueFontSize = '14px',
axisMarkerFontSize = '11px',
axisFontSize = '11px',
xAxis = [0, 50],
// data keys
valueKey = 'value',
targetKey = 'target',
targetDir = 'targetDir',
labelKey = 'label',
// fills
barBgd = '#ccc',
axisMarkerFill = '#ccc',
barFill = '#2166ac',
barFillBelow = '#b2182b',
labelFill = '#fff',
markerFill = '#454545',
showMarker = true,
showPctAxis = true
} = {}) {

// set the dimensions and margins of the graph
const w = width + margin.left + margin.right;
const h = height + margin.top + margin.bottom;


// create a container
// do a destroy
bind.selectAll('div').remove();
const divContainer = bind.append('div')
.attr('class', 'bars-wrap clearfix');
// const titleContainer = divContainer.append('div')
// .attr('class', 'title-container ')
const svgContainer = divContainer.append('div')
.attr('class', 'svg-container');

// setup scales
const yScale = d3.scaleLinear()
.domain(xAxis)
.range([barHeight, 0]);

const svg = svgContainer.append('svg')
.attr('width', w)
.attr('height', h)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);

const group = svg.selectAll('g')
.data(data)
.join('g')
.attr('class', (d, i) => 'bar-group g-' + i)
.attr('transform', (d, i) => `translate( ${i * (barWidth * spacing)} , 0 )`);

// append bgd bar
group.append('rect')
.attr('class', `bar bar-bgd`)
.attr('height', barHeight)
.attr('x', 0)
.attr('y', 0)
.attr('width', barWidth)
.style('fill', barBgd)
.style('opacity', opacity)
.style('pointer-events', 'none');

// append bar
group.append('rect')
.attr('class', `bar bar-top`)
.attr('height', d => barHeight - yScale(d[valueKey]))
.attr('x', 0)
.attr('y', d => yScale(d[valueKey]))
.attr('width', barWidth)
.style('fill', d => {
if (d[targetDir] === 'above' && d[valueKey] >= d[targetKey]) {
return barFill;
} else if (d[targetDir] === 'above' && d[valueKey] < d[targetKey]) {
return barFillBelow;
} else if (d[targetDir] === 'below' && d[valueKey] > d[targetKey]) {
return barFillBelow;
} else if (d[targetDir] === 'below' && d[valueKey] <= d[targetKey]) {
return barFill;
}
})
.style('opacity', opacity)
.style('pointer-events', 'none');

// append markers
if (showMarker) {
group.append('line')
.attr('class', 'maker bar-marker-top')
.attr('x1', barWidth)
.attr('x2', barWidth + 5)
.attr('y1', d => yScale(d[targetKey]))
.attr('y2', d => yScale(d[targetKey]))
.style('fill', 'none')
.style('stroke', markerFill)
.style('stroke-width', 1)
.style('opacity', 0.7)

// append value MARKER label
group.append('text')
.attr('class', 'labels sqbar-label')
.attr('y', d => yScale(d[targetKey]))
.attr('x', barWidth + 15)
.attr('dx', -10)
.attr('dy', 3)
.style('font-size', axisMarkerFontSize)
.style('letter-spacing', '1px')
.style('fill', markerFill)
.style('pointer-events', 'none')
.style('text-anchor', 'right')
.text(d => d[targetKey] + '%')
}
// append markers
if (showPctAxis) {
group.call(axisMarker, xAxis[0], axisMarkerFill)
group.call(axisMarker, xAxis[1], axisMarkerFill)
};

// append value
group.append('text')
.attr('class', 'sqbar-label')
.attr('y', barHeight)
.attr('x', barWidth / 2)
.attr('dx', 0)
.attr('dy', barHeight * -0.1)
.style('font-size', valueFontSize)
// .style('fill', d3.color(config.barFill).darker())
.style('fill', labelFill)
.style('pointer-events', 'none')
.style('text-anchor', 'middle')
.text(d => d[valueKey] + '%');

group.append('text')
.attr('class', 'sqbar-title')
.attr('y', -15)
.attr('x', barWidth / 2)
.attr('dx', 0)
.attr('dy', 0)
.style('font-size', labelFontSize)
.style('fill', '#ccc')
// .style('pointer-events', 'none')
.style('text-anchor', 'middle')
.text(d => d[labelKey]);

// markers for target and axis %
function axisMarker (selection, value, col) {
// append bar marker
selection.append('line')
.attr('class', 'sqbar-marker-top')
.attr('x1', 0)
.attr('x2', -3)
.attr('y1', yScale(value))
.attr('y2', yScale(value))
.style('fill', 'none')
.style('stroke', col)
.style('stroke-width', 1)
.style('opacity', 0.7);

// append value MARKER label
selection.append('text')
.attr('class', 'sqbar-label')
.attr('y', yScale(value))
.attr('x', -5)
.attr('dx', 0)
.attr('dy', 3)
.style('font-size', axisFontSize)
// .style('letter-spacing', '1px')
.style('fill', col)
// .style('pointer-events', 'none')
.style('text-anchor', 'end')
.text(value);
}

return svg;

}
Insert cell
<hr>
<link href="https://fonts.googleapis.com/css?family=Space+Mono" rel="stylesheet">
<style>
text {
font-family:'Space Mono',monospace;
}
</style>
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more