Vue - Combo Component

Made with std Inputbox over Select combo. Optionlist not alterable limitation.

by Ben Clayton

HTML

<script src="https://unpkg.com/vue"></script>
 === Vue JS - Component Example ====
<div id="mycombo">
  <combo-widget :context='$data' property='myvalue' :list='list'></combo-widget>
  <br/> Non constrained Combo value:<b> {{myvalue}}</b>
  <br/> Combo List Offered: <b>{{list}}</b>
</div>


<script id="tmpl-combo-widget" type="text/x-template">
  <div class="combo-widget">
    <select class="cwselect" v-model="context[property]">
      <option v-for="opt in list" :value="opt">{{opt}}</option>
    </select>
    <div class="cwplaceholder txt" ><span class="pstart">{{pstart}}</span><span class="pend">{{pend}}</span></div>
    <input class="cwselectinput txt" type="text" v-model="context[property]" v-on:change="change" />
    {{placeholder}}
  </div>
</script>

CSS

.combo-widget {
  position:relative;
}
.combo-widget .cwselect {
  position: relative;
  width: 230px;
  height: 26px;
}
.combo-widget .txt {
  font-family: "Times New Roman", Times, serif;
  font-size:14px;
}

.combo-widget .cwselectinput {
  position: absolute;
  z-index: 3;
  width: 200px;
  height: 22px;
  top:0px;
  left:0px;
  border-top-left-radius: 3px;
  border-bottom-left-radius: 3px;
  border: 1px solid #C0C0C0;
  border-right: 0;
  background-color: rgba(0,0,0,0);
}


.combo-widget .cwplaceholder {
  position: absolute;
  z-index: 2;
  width: 200px;
  height: 22px;
  top:4.3px;
  left:2px;
  color:gray;
  background-color: rgb(248, 248, 248);
}
.combo-widget .pstart {
    color:white;
}
.combo-widget .pend {
    position:relative;
    color:gray;
    top:1px;
}


input:focus,
select:focus {
  outline: none !important;
}

JavaScript

Vue.component('combo-widget', {
  template: "#tmpl-combo-widget",
  props: {
    "context": {
      type: Object, // expect object like { name: "Size", value: "Large" }
      required: true
    },
    "property": {
      type: String, // expect string like "name" or "value"
      required: true
    },
    "list": {
      type: Array // expect ["Video","Music","Audio"]
    }
  },
  data: function() {
    return {}
  },
  methods: {
    change: function(e) {
      var v = e.target.value;
      var i = this.list.map(function(x){return x.toLowerCase()}).indexOf(v.toLowerCase());
      if(i>-1){
      	 Vue.set(this.context,this.property,this.list[i]);
      } else {
        this.list.push(v);
      }
    }
  },
  computed: {
    pstart: function() {
    	return "T";//this.context[this.property];
    },
    pend: function() {
    	return "hree";//this.context[this.property];
    }
  },

  created() {
    //console.log('list:',this.list);
  }
});
//---------------------------------------------------------
new Vue({
  el: '#mycombo',
  data: {
    myvalue: "Three",
    list: ["One", "Two", "Three"]
  }
});