JSFiddle - React, Tailwind, and code Playground

D3V6 Drawboard

by PhilM

HTML

<script src="https://d3js.org/d3.v3.min.js"></script>
<svg class="canvas">

</svg>
<input id="isSnap" type="checkbox" checked/>Snap to grid

CSS

.canvas{
  width: 500px;
  height: 400px;
  background-color:white;
  display:block;
}

polygon{
  stroke: blue;
  stroke-width: 1px;
  fill: transparent;
}

polygon:hover{
  fill: #C2E0F2;
}

.grip{
  fill: gray;
  stroke-width:0px;
  cursor: move;
}

.gripsGroup{
  opacity: 0;
}

.panel:hover .gripsGroup, .gripsGroup->.grip:hover{
  opacity:1;
}

JavaScript

var panels = [{
	offset : {x:50, y:100},
  geometry: [{x:0, y:0},
  	{x:120, y:0}
    {x:0, y:260},
    {x:120, y:260}]
}];

var canvas = d3.select(".canvas");
var grid = 10;
//var isShiftDown = false;

//d3.select("body").on("keydown")

var dragListener = d3.behavior.drag()
	.origin(function(d){ return d; })
	.on("drag", dragMove);
  
var isSnap = d3.select("#isSnap");

function CreatePanels(){  
  var groups = canvas.selectAll(".panel").data(panels);
  var newGroups = groups.enter().append("g").attr("class", "panel");
  newGroups.append("polygon");
  var grippers = newGroups.append("g").attr("class", "gripsGroup");
  grippers.selectAll(".grip")
  	.data(function(p, i){
  		return {panel: p, point:p.geometry[i]}; 
    })
    .classed("grip", true).call(dragListener);
  /*grippers.append("circle").classed("grip grip-tl", true)
  	.datum(function(p,i){ return {panel:p, point:p.topLeft; }})
    .call(dragListener);
  grippers.append("circle").classed("grip grip-tr", true)
  	.datum(function(p,i){ return {panel:p, point:p.topRight; }})
    .call(dragListener);
  grippers.append("circle").classed("grip grip-bl", true)
  	.datum(function(p,i){ return {panel:p, point:p.bottomLeft; }})
    .call(dragListener);
  grippers.append("circle").classed("grip grip-br", true)
  	.datum(function(p,i){ return {panel:p, point:p.bottomRight; }})
    .call(dragListener);*/
}

function UpdatePanels(){
  var groups = canvas.selectAll(".panel");
  groups.attr("transform", function(p, i){
    	return "translate(" + pointToText(p.offset) + ")";
    });
  /*groups.selectAll("polygon")
  	.attr("points", function(p, i){
    	return pointToText(p.topLeft) + " " + pointToText(p.topRight) + " " + 
      pointToText(p.bottomRight) + " " + pointToText(p.bottomLeft);
    });*/
  groups.selectAll("polygon")
  	.attr("points", function(p, i){
    	return d3.map(p.geomoetry, function(point){
      	return [point.x, point.y].join(",")
      }).join(" ");
    });
  var grips =...