JSFiddle - React, Tailwind, and code Playground

HTML

<script src="//d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>

CSS

.axis line {
  stroke-opacity: 0.3;
  shape-rendering: crispEdges;
}

JavaScript

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var x = d3.scaleLinear()
    .domain([-1, width + 1])
    .range([-1, width + 1]);

var y = d3.scaleLinear()
    .domain([-1, height + 1])
    .range([-1, height + 1]);

var xAxis = d3.axisBottom(x)
    .ticks((width + 2) / (height + 2) * 10)
    .tickSize(height)
    .tickPadding(8 - height);

var yAxis = d3.axisRight(y)
    .ticks(10)
    .tickSize(width)
    .tickPadding(8 - width);


var gX = svg.append("g")
    .attr("class", "axis axis--x")
    .call(xAxis);

var gY = svg.append("g")
    .attr("class", "axis axis--y")
    .call(yAxis);

svg.append('circle')
		.attr('r', 15)
    .attr('cx', 100)
    .attr('cy', 100);
    
svg.call(d3.zoom()
         .scaleExtent([1, 40])
         .translateExtent([[-100, -100], [width + 90, height + 100]])
         .on("start", zoomstart)
         .on("end", zoomend)
         .on("zoom", zoomed));

var mousePos = null;
function zoomstart(){
	mousePos = d3.mouse(this);
}

function zoomend(){
	var m = d3.mouse(this);
	if (mousePos[0] ===  m[0] && mousePos[1] === m[1]){
  	alert('mouseup');
  }
}

function zoomed() {
  svg.attr("transform", d3.event.transform);
  gX.call(xAxis.scale(d3.event.transform.rescaleX(x)));
  gY.call(yAxis.scale(d3.event.transform.rescaleY(y)));
}

//document.querySelector('svg').addEventListener('mouseup', function(){ alert(1) })
///////////////////////////////////////