Public
Edited
Aug 3, 2023
Fork of square bars
1 fork
1 star
Insert cell
Insert cell
visFlow = html`<div class="vis-sf"></div>`
Insert cell
Insert cell
Insert cell
Insert cell
flowDiagram(data, {
bind: d3.select(visFlow), // pass in the selection
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
visWrap = html`<div class="vis-sf-multi flex flex-wrap justify-center p2 center"></div>`
Insert cell
{ // have a way of updating the cell put not creating extra dom elements
if (d3.select(visWrap).empty()) { // don't reload on any cell refreshes
testMultipleRender(dataMultiple, visWrap);
} else {
d3.select(visWrap).selectAll('div').remove();
testMultipleRender(dataMultiple, visWrap);
}
}
Insert cell
function testMultipleRender(data, sel) { // render dom and bind svg visual to it
return data.forEach((d, i) => {
const div = d3.select(sel).append('div').attr('class', 'sf-vis pt2 m1 ref-' + i).style('background', '#fff');
d3.select('.ref-' + i).selectAll('h3').remove(); // tidy for refreshes on page
d3.select('.ref-' + i).append('h3').attr('class', 'label').text('TEAM-0' + i)
flowDiagram(data[i], {
bind: d3.select(sel).select('.ref-' + i), // pass in the selection
domainRange: [0, 100] // default is [0, 50]
})
})
}
Insert cell
dataMultiple = d3.range(4).map(d => { // some dummy data for multiple render test
return data.map(o => {
return {
value: _.random(30, 100),
label: o.label,
targetDir: _.sample(['above', 'below']),
target: _.random(0, 100)
}
})
})
Insert cell
Insert cell
function flowDiagram(data, {
bind = null, // pass in a d3 selection i.e d3.select('class/div')
margin = { top: 0, right: 0, bottom: 0, left: 0 },
width = 410,
height = 410,
edgeStroke = 4, // connecting lines thickness of stroke
centerSquare = 115, // size of center square
squareConnector = centerSquare * 0.15, // size of small square connecting nodes
targetDirection = 'above', // target can be below/above
centerSquareFill = bgdCol2,
domainRange = [0, 50] // pass in percentage range e.g max [0, 100]
} = {}) {

// 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 svgContainer = divContainer.append('div')
.attr('class', 'svg-container');

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

const centerH = (w / 2) - (centerSquare / 2); // horizontal center
const centerV = (h / 2) - (centerSquare / 2); // vertical center
const squareSize = 60; // size of the data square

// left and right node
svg.append('rect')
.attr('width', centerSquare + squareConnector)
.attr('height', squareConnector)
.attr('x', centerH -(squareConnector/2))
.attr('y', centerV + (centerSquare / 2) - (squareConnector/2))
.attr('fill', centerSquareFill);

// top and bottom node
svg.append('rect')
.attr('width', squareConnector)
.attr('height', centerSquare + squareConnector)
.attr('y', centerV -(squareConnector/2))
.attr('x', centerH + (centerSquare / 2) - (squareConnector/2))
.attr('fill', centerSquareFill);

// edge/connector lines
// horizontal edges
svg.append('rect')
.attr('width', centerSquare + squareConnector + 50)
.attr('height', edgeStroke)
.attr('x', centerH -(squareConnector/2) - 25)
.attr('y', centerV + (centerSquare / 2) - (edgeStroke/2))
.attr('fill', centerSquareFill)
.style('stroke-linecap', 'round');
// vertical edges
svg.append('rect')
.attr('width', edgeStroke)
.attr('height', centerSquare + squareConnector + 50)
.attr('x', centerH + (centerSquare / 2) - (edgeStroke/2))
.attr('y', centerV - (squareConnector/2) - 25)
.attr('fill', centerSquareFill)
.style('rx', '2');

// background/center shape
svg.append('rect')
.attr('width', centerSquare)
.attr('height', centerSquare)
.attr('x', centerH)
.attr('y', centerV)
.attr('rx', 5)
.attr('fill', centerSquareFill);
// some helpers for each of the data square locations
const middleSquare = svg.append('g')
.attr('transform', `translate(${w/2 - (squareSize / 2)}, ${h/2 - 25})`);

const topSquare = svg.append('g')
.attr('transform', `translate(${w/2 - (squareSize / 2)}, ${h/2 - 100 - 60})`);

const botSquare = svg.append('g')
.attr('transform', `translate(${w/2 - (squareSize / 2)}, ${h/2 + 100})`);

const leftSquare = svg.append('g')
.attr('transform', `translate(${w/2 - ((squareSize * 2) + 40)}, ${h/2 - 25})`);
const rightSquare = svg.append('g')
.attr('transform', `translate(${w/2 + (squareSize + 40)}, ${h/2 - 25})`);


// render the squares with data

squareBar([data[0]], {
bind: middleSquare, // pass in the selection
barWidth: squareSize,
titleFill: '#555',
titlePadding: 9,
xAxis: domainRange
});

squareBar([data[1]], {
bind: topSquare, // pass in the selection
barWidth: squareSize,
xAxis: domainRange
});

squareBar([data[2]], {
bind: botSquare, // pass in the selection
barWidth: squareSize,
titlePadding: 25,
titleTop: false,
xAxis: domainRange
});

squareBar([data[3]], {
bind: leftSquare, // pass in the selection
markerRight: false, // display marker on the left
barWidth: squareSize,
xAxis: domainRange
});

squareBar([data[4]], {
bind: rightSquare, // pass in the selection
barWidth: squareSize,
xAxis: domainRange
});
}
Insert cell
Insert cell
<hr>
<link href="https://fonts.googleapis.com/css?family=Space+Mono" rel="stylesheet">
<link href="https://unpkg.com/basscss@8.0.2/css/basscss.min.css" rel="stylesheet">
<style>
text {
font-family:'Space Mono',monospace;
font-weight: 600;
font-size: 11px;
}
.vis-sf-multi {
background: #efefef;
}
.vis-sf-multi, .vis-sf-multi h3 {
font-family:'Space Mono',monospace;
color: ${aboveCol};
font-weight: 600;
}
</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