JSFiddle - React, Tailwind, and code Playground

by Twisty

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqwidgets/12.1.2/jqwidgets/jqx-all.js"></script>
<body>
  <div class="draggable" id="knob"></div>
  <div id="container">
    <div id="droppable">
      <img class='img_source' src="https://helpx.adobe.com/content/dam/help/en/photoshop/using/convert-color-image-black-white/jcr_content/main-pars/before_and_after/image-before/Landscape-Color.jpg" />
    </div>
  </div>
  <div id="info"></div>

  <div class="hide" id="template">
    <form id="knob-form-0">
      <legend>Position</legend>
      <input type="text" class="drag-x" name="dragItemPositionX[]" id="dragItemPositionX-0" />
      <input type="text" class="drag-y" name="dragItemPositionY[]" id="dragItemPositionY-0" />
      <fieldset>
        <input type="button" id="saveKnob-0" value="Save Knob" />
        <input type="button" id="removeKnob-0" value="Remove Knob" />
      </fieldset>
    </form>
  </div>
</body>

CSS

html {
  overflow: -moz-scrollbars-vertical;
  overflow-y: scroll;
}

.dial {
  cursor: pointer;
  width: 60px;
}

#container {
  background: #fbfbfb;
  float: left;
  padding: 20px;
  margin-top: 20px;
  width: 100%;
  width: calc(100% - 40px);

}

#droppable {
  float: left;
}

.img_source {
  display: block;
  width: 200px;
}

.gear_grid {
  background: #f5f5f5;
  height: 600px;
}

#device {
  float: left;
  padding-top: 20px;
  width: 100%;

}

#info {
  padding: 20px;
  background: #f5f5f5;
  float: left;
  width: calc(100% - 40px);
  box-sizing: border-box;
  width: 100%;
}

#info form {
  display: none;
}

#drop_del {
  background: #FFF;
  height: 200px;
  position: relative;

}

.editable {
  position: absolute !important
}

.input_value {
  width: 60px;
  font-size: 11px;
  display: block;
  text-align: center;
  border: none;
  background: none;
}


#imagePreview {
  width: 180px;
  height: 180px;
  background-position: center center;
  background-size: cover;
  -webkit-box-shadow: 0 0 1px 1px rgba(0, 0, 0, .3);
  display: inline-block;
}

.upload-msg {
  text-align: center;
  padding-top: 70px;
  padding-left: 30px;
  padding-right: 30px;
  font-size: 22px;
  color: #aaa;
  width: 240px;
  height: 200px;
  margin: 10px auto;
  border: 1px solid #aaa;
}

.upload.ready #upload-section {
  display: block;

}

.upload.ready .buttons .upload-result,
.upload.ready .buttons .reset {
  display: inline;
}

.upload #upload-section,
.upload .buttons .upload-result,
.upload .buttons .reset,
.upload.ready .upload-msg {
  display: none;

}

#upload {
  position: absolute;
  top: -1000px;

}

.upload.ready {
  background: #f1f1f1;
  padding: 20px;
  border: 1px solid grey;
}

#btn {
  background-color: #189094;
  color: white;
  padding: 10px 15px;
  border-radius: 3px;
  border: 1px solid rgba(255, 255, 255, 0.5);
  font-size: 16px;
  cursor: pointer;
  text-decoration: none;
  text-shadow: none;
  cursor: pointer;
  width: auto;
  margin: 0 auto;
}

#result {
 ...

JavaScript

$(function() {
  function getId() {
    var dt = new Date().getTime();
    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = (dt + Math.random() * 16) % 16 | 0;
      dt = Math.floor(dt / 16);
      return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
    return uuid;
  }

  function makeKnobDrag(item) {
    return $(item).draggable({
      containment: "#droppable",
      drag: function(event, ui) {
        $("#knob-form-" + $(item).data("form") + " .drag-x").val(ui.position.left);
        $("#knob-form-" + $(item).data("form") + " .drag-y").val(ui.position.top);
      }
    });
  }

  function getActiveKnobPosition() {
    return $(".active_knob").closest(".jqx-knob").position();
  }

  function makeForm(target, uuid) {
    var c = $(".active_knob").closest(".jqx-knob").data("form");
    var form = $("<form>", {
      class: "pure-form knob",
      name: uuid,
      "data-id": uuid,
      id: "knob-form-" + c
    }).appendTo(target);
    form.append($("#template form").children().clone());
    form.find('input').each(function(i, el) {
      $(el).attr({
        name: $(el).attr("name") + "[]",
        "data-id": uuid,
        id: $(el).attr("id").substring(0, $(el).attr("id").indexOf("-")) + c
      });
    });
    $("form.knob").hide();
    var kPos = getActiveKnobPosition();
    $(".drag-x", form).val(kPos.left);
    $(".drag-y", form).val(kPos.top);
    form.show();
  }


  // Create default knob
  $('#knob').jqxKnob({
    width: 34,
    height: 34,
    disabled: true,
    value: 0,
    min: 0,
    max: 100,
    startAngle: 120,
    endAngle: 420,
    snapToStep: true,
    rotation: 'clockwise',
    style: {
      stroke: '#000',
      strokeWidth: 1,
      fill: {
        color: '#fff'
      }
    },
    pointer: {
      type: 'line',
      thickness: 4,
      style: {
        fill: "#00a4e1",
        stroke: "#00a4e1"
      },
      size: '70%',
      offset: '0%'
    }
  });

  //Drag default knob in...