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

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more