JSFiddle - React, Tailwind, and code Playground

by gdicerbo

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>

CSS

.axis text {
	font: 10px sans-serif;
}
.axis path, .axis line {
	fill: none;
	stroke: #000;
	shape-rendering: crispEdges;
}

JavaScript

function barStack(seriesData) {
	var l = seriesData[0].length
	while (l--) {
		var posBase = 0; // positive base
		var negBase = 0; // negative base

		seriesData.forEach(function(d) {
			d = d[l]
			d.size = Math.abs(d.y)
			if (d.y < 0) {
				d.y0 = negBase
				negBase -= d.size
			} else
			{
				d.y0 = posBase = posBase + d.size
			}
		})
	}
	seriesData.extent = d3.extent(
		d3.merge(
			d3.merge(
				seriesData.map(function(e) { 
					return e.map(function(f) { return [f.y0,f.y0-f.size] }) 
				})
			)
		)
	)
}

/* Here is an example */

// each series is an array of objects
// our data is in turn an array of those series arrays
// which is to say
// an array of arrays of objects
var data = [
	[ {y:-14202756}, {y:0}, {y:0},  {y:0} ],
	[ {y:0}, {y:2242378}, {y:4168212},  {y:4244142} ],
	[ {y:0}, {y:-515070}, {y:-704541},  {y:-720288}  ]
]

var h = 500;
var w = 500;
var margin = 20;
var color = d3.scale.category10();

var x = d3.scale.ordinal()
	.domain(['2018', '2019', '2020', '2021'])
	.rangeRoundBands([ margin, w - margin ], .1)

var y = d3.scale.linear()
	.range([h-margin,0+margin]);

var xAxis = d3.svg.axis()
	.scale(x)
	.orient("bottom")
	.tickSize(6, 0);

var yAxis = d3.svg.axis()
	.scale(y)
	.orient("left");

barStack(data);
y.domain(data.extent);

svg = d3.select("body")
	.append("svg")
	.attr("height", h)
	.attr("width", w)

svg.selectAll(".series")
	.data(data)
	.enter()
	.append("g")
	.classed("series", true)
	.style("fill", function(d,i) { return color(i) })
	.style("opacity", 0.8)
		.selectAll("rect")
		.data(Object)
		.enter()
		.append("rect")
			.attr("x", function(d, i) { return x(x.domain()[i]) })
			.attr("y", function(d) { return y(d.y0) })
			.attr("height", function(d) { return y(0) - y(d.size) })
			.attr("width", x.rangeBand())
			.on("mouseover", function() { tooltip.style("display", null); })
  		.on("mouseout", function() { tooltip.style("display", "none"); })
  		.on("mousemove", function(d) {
    		var xPosition =...