Vue - List

Accepts pasted data, Vue updates html outside of template

by Ben Clayton

HTML

<script src="https://unpkg.com/vue"></script>
 === Name Blocks ====<br/><br/>
<div id="outside"></div>

<div id="interface">
</div>


<script id="vue-tmpl-interface" type="text/x-template">
  <div class="interface">
    <div class="preview">
      <div class="line1">{{ previewline('line1') }}
      </div>
      <div class="line2">{{ previewline('line2') }}
      </div>
    </div>

    <div class="list-wrapper">
      Names can be typed into the boxes below, or copy or drag from a two column Word table or Excel spreadsheet into box here.
      <div tabindex=-1 id='pasteDiv' contenteditable='true'>Paste or Drop here</div>
      <div class="header">
        <div class="col">Line1</div>
        <div class="col">Line2</div>
      </div>

      <div class="scrolling-list-wrapper">
        <div v-for="(row,index) in rows" :key="index" :row="row" v-bind:class="{ selected: isSelected(index) }">
          <input class="line1" v-bind:class="{ truncated: isTruncated(index,'line1') }" type="text" v-model="row['line1']" v-on:focus="focus(index)" maxlength="22" />
          <input class="line2" v-bind:class="{ truncated: isTruncated(index,'line2') }" type="text" v-model="row['line2']" v-on:focus="focus(index)" maxlength="22" />
          <button tabindex=-1 v-on:click="namedel(index)">Del</button>
        </div>
        <button tabindex=-1 v-on:click="nameadd">Add</button>
      </div>
      <div class="qtyofnames">Qty of Names: {{quantity}} (cost £{{quantityPrice}}) </div>


    </div>
  </div>
</script>

CSS

.interface {}

.qtyofnames {
  margin-bottom: 15px;
}

.pastebutton {
  margin-bottom: 15px;
}

.list-wrapper {
  float: right;
  width: 530px;
}

.scrolling-list-wrapper {
  float: right;
  border: 1px solid gray;
  background-color: #F0F0F0;
  padding: 10px;
  width: 520px;
  height: 100px;
  overflow: auto;
  margin-bottom: 10px;
}

.scrolling-list-wrapper .line1,
.scrolling-list-wrapper .line2 {
  background-color: #E0E0E0;
  margin-bottom: 5px;
  width: 220px
}

.scrolling-list-wrapper .line1.truncated,
.scrolling-list-wrapper .line2.truncated {
  background-color: red;
}

.scrolling-list-wrapper div.selected input {
  background-color: #FFFFFF;
}

.scrolling-list-wrapper .line1:focus,
.scrolling-list-wrapper .line1:focus,
#pasteDiv:focus {
  outline: none !important;
}

.preview {
  margin-top: 20px;
  float: left;
  border: 1px solid gray;
  width: 380px;
  height: 100px;
  padding: 20px;
}

.preview .line1,
.preview .line2 {
  background-color: white;
  width: 100%;
  font-size: 22px;
  font-family: "Times New Roman";
  text-align: center;
  margin: 5px auto;
}

.list-wrapper .header .col {
  display: inline-block;
  width: 200px;
  margin-left: 30px;
}

#pasteDiv {
  width: 100px;
  height: 30px;
  background-color: white;
  border: 1px dashed gray;
  padding: 10px;
  text-align: center;
  margin-bottom: 20px;
}

#pasteDiv:hover {
  border: 1px solid green;
  background-color: #D0D0FF;
  color: black;
}

JavaScript

//---------------------------------------------------------

var vm = new Vue({ // create a root Vue instance
  el: '#interface', // this div gets replaced when View is rendered
  template: '#vue-tmpl-interface',
  data: {
    rows: [{
      line1: "",
      line2: ""
    }],
    selectedindex: 0
  },
  watch: {
    rows: {
      handler: function(val, oldVal) {
        document.getElementById('outside').innerHTML = val[this.selectedindex].line1 + val[this.selectedindex].line2;
      },
      deep: true
    },
    selectedindex: {
      handler: function(val, oldVal) {
        document.getElementById('outside').innerHTML = this.rows[val].line1 + this.rows[val].line2;
      },
      deep: true
    }
  },

  computed: {
    quantity: function() {
      return this.rows.length
    },
    quantityPrice: function() {
      return this.rows.length * 35;
    }

  },
  methods: {
    isSelected: function(index) {
      console.log("sle", index);
      return this.selectedindex == index;
    },
    isTruncated: function(index, line) {
      console.log("trunc", index);
      return this.rows[index][line].length > 22;
    },
    previewline(line) {
      return this.rows[this.selectedindex][line].substring(0, 22);
    },
    nameadd() {
      console.log("Add");
      this.rows.push({
        line1: "",
        line2: ""
      });
    },
    namedel(index) {
      console.log("Delete", index);
      this.selectedindex = (index > 0) ? index - 1 : 0;
      if (this.rows.length == 1) {
        this.rows[0].line1 = "";
        this.rows[0].line2 = "";
      } else {
        this.rows.splice(index, 1);
      }
    },
    cleanlist() {
      for (var x = this.rows.length - 1; x > -1; x--) {
        if (this.rows[x].line1 == "" && this.rows[x].line2 == "") {
          this.namedel(x)
        }
      }
    },
    focus(index) {
      console.log(index);
      this.selectedindex = index;
    },
    paste(data) {
      console.log("Paste", data);
      if (data) {
        var...