JSFiddle - React, Tailwind, and code Playground

by Oski Krawczyk

HTML

<form id="categories">
  <fieldset>
    <div v-bind:class="'categoryItem accent_' + category.id" v-bind:data-id="category.id" v-for="(category, index) in categories">
      <template v-if="category.used">
        <input type="text" v-bind:name="'account[categories][' + category.id + ']'" v-bind:value="category.name" placeholder="Category name">
      </template>
      <template v-else>
        <input type="text" v-bind:name="'account[new_categories][' + category.id + ']'" v-bind:value="category.name" placeholder="Category name">
      </template>
      <a href="#" v-on:click="removeCategory(index, category)">Remove</a>
    </div> <!-- /categoryItem -->

    <div class="categoryItem" v-show="counter <= 9">
      <input type="text" placeholder="Category name" v-model="newCategory">
      <a href="#" v-on:click="addCategory">Add</a>
    </div> <!-- /categoryItem -->

  </fieldset>
  
  <div v-bind:class="'categoryItem accent_' + category.id" v-bind:data-id="category.id" v-for="(category, index) in categories">
      <input type="text" v-bind:name="'account[categories][' + category.id + ']'" v-bind:value="category.name" placeholder="Category name">
    </div> <!-- /categoryItem -->

</form>

JavaScript

var Categories;

window.Settings = {
  categories: [
    {
      name: "New",
      id: 1,
      used: true
    }, {
      name: "Improvements",
      id: 2,
      used: true
    }, {
      name: "Fix",
      id: 3,
      used: true
    }, {
      name: "Update 1",
      id: 4,
      used: true
    }
  ]
};

Categories = new Vue({
  el: "#categories",
  data: {
    newCategory: "",
    categories: window.Settings.categories,
    counter: 0,
    availableIds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    disabled: false
  },
  mounted: function() {
    return this.$nextTick(function() {
      var cat, i, index, len, ref;
      ref = this.categories;
      for (i = 0, len = ref.length; i < len; i++) {
        cat = ref[i];
        index = this.availableIds.indexOf(cat.id);
        this.availableIds.splice(index, 1);
      }
      return this.counter = this.categories.length;
    });
  },
  methods: {
    getId: function() {
      return this.availableIds.splice(0, 1);
    },
    sortIds: function() {
      return this.availableIds.sort(function(a, b) {
        return a - b;
      });
    },
    returnId: function(index, id) {
      this.categories.splice(index, 1);
      this.counter = this.counter - 1;
      this.availableIds.push(id);
      return this.sortIds();
    },
    addCategory: function(event) {
      var text;
      event.preventDefault();
      event.stopPropagation();
      text = this.newCategory.trim();
      if (text) {
        this.counter = this.counter + 1;
        this.categories.push({
          name: text,
          id: this.getId(),
          used: false
        });
        return this.newCategory = "";
      }
    },
    removeCategory: function(index) {
      var categoryId, message;
      categoryId = this.categories[index].id;
      message = "This category is in use. All changelogs under\nthis category will unassigned from it. Proceed?\"";
      if (this.categories[index].used) {
        if (confirm(message)) {
          return...