JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/0.12.5/vue.min.js"></script>
  <div id="graph">
    <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300" version="1.1" v-on="click: add">
      <g>
        <circle
          v-on="click: selectFunc"
          v-repeat="nodes"
          id="{{id}}"
          cx="{{cx}}"
          cy="{{cy}}"
          r="30"
          v-class="selected: selected"
        >
        </circle>
      </g>
    </svg>

    <pre>{{ $data | json }}</pre>

    <button
      class="btn btn-default"
      v-on="click:
        (
          funcs.add = ! funcs.add,
          funcs.move = false,
          funcs.remove = false
        )"
      v-class="active: funcs.add"
      >
        Add Node
      </button>
    <button
      class="btn btn-default"
      v-on="click:
        (
          funcs.move = ! funcs.move,
          funcs.add = false,
          funcs.remove = false
        )"
      v-class="active: funcs.move"
    >
      Move
    </button>
    <button
      class="btn btn-default"
      v-on="click:
        (
          funcs.remove = ! funcs.remove,
          funcs.add = false,
          funcs.move = false
        )"
      v-class="active: funcs.remove"
    >
      Remove
    </button>
  </div>

CSS

circle {
  fill: blue;
  stroke: black;
  stroke-width: 3;
}

#raw {
    position: absolute;
    top: 0;
    left: 300px;
}

.selected {
  stroke-width: 5;
}

JavaScript

new Vue({
	el: '#graph',
	data: {
		funcs: {
			add: false,
			move: false,
			remove: false
		},
		nodes: [
			{
				cx: 100,
				cy: 100,
				id: 0,
				selected: false,
				ini: false,
				final: false
			}
		],
		posMouse: {
			x: 0,
			y: 0
		},
		selected: {
			sx: null,
			sy: null,
			id: null
		}
	},
	methods: {
		selectFunc: function (node) {
			node.preventDefault();

			if (this.funcs.move) {
				this.move(node);
			} else if (this.funcs.remove) {
				this.remove(node);
			}
		},
		add: function (node) {
			node.preventDefault();

			// Recebe tag do objeto a ser clicado
			var parentPosition = getPosition(node.currentTarget);
			// Posição do mouse em X
			this.posMouse.x = node.clientX - parentPosition.x;
			// Posição do mouse em Y
			this.posMouse.y = node.clientY - parentPosition.y;

			// Adiciona novo nó caso botão Add ativado
			if (this.funcs.add) {
				this.nodes.push({
					cx: this.posMouse.x, // X set para posição do mouse
					cy: this.posMouse.y, // Y set para posição do mouse
					id: this.nodes.length, // ID do nó
					selected: false, // Se nó esta selecionado
					ini: false, // Nó é inicial?
					final: false // Nó é final?
				});
			}
		},
		move: function (node) {
			node.preventDefault();

			// Recebe a seleção do nó
			this.selected.id = parseInt(node.target.id);

			// Seleção do nó (visual)
			this.nodes.forEach(function(id, index){
				if(id.selected === true)
					id.selected = false;
			});

			var index = getIndex(this.nodes, this.selected.id);
			this.nodes[index].selected = ! this.nodes[index].selected;
		},
		remove: function (node) {
			node.preventDefault();
			this.selected.id = parseInt(node.target.id);

			var index = getIndex(this.nodes, this.selected.id);

			//Remove nó pelo index
			this.nodes.$remove(index);
		}
	}
});

// Método para pegar a posição correta do mouse
function getPosition(element) {
    var xPosition = 0;
    var yPosition = 0;

    while (element) {
        xPosition += (element.offsetLeft...