Knockout Prototype Meta Editor

by Ben Clayton

HTML

<div style="height:100px"></div>
<div data-bind="foreach: keywords">
  <div class="row">
    <combo-widget params="value: parent"></combo-widget>
    <combo-widget params="value: child"></combo-widget>
  </div>
</div>
<button data-bind="click: add">Add</button>

CSS

.ncselect {
  position: relative;
  display:inline-block;
}

.optionlist {
  border: 1px solid gray;
  width: 125px;
  max-height: 200px;
  overflow: auto;
  position: absolute;
  z-index: 2;
  bottom: 28px;
  background-color: #FFFFFF;
}

.selectinput {
  width: 100px;
}

.ipwrapper {
  display: inline-block;
  position: relative;
}

.ipwrapper input {
  height: 20px;
}

.combotrigger {
  font-family: arial;
  font-weight: bold;
  line-height: 24px;
  display: inline-block;
  position: relative;
  background-color: white;
  width: 20px;
  height: 24px;
  cursor: pointer;
  text-align: center;
  border: 1px solid #C0C0C0;
  left: -4px;
  top: 1px;
}

JavaScript

ko.components.register('combo-widget', {
  viewModel: function(params) {
    var self = this;
    self.value = ko.observable(params.value);
    self.optionsOpen = ko.observable(false);
    self.people = ko.observableArray([{
      firstName: "Bert",
      lastName: "Bertington"
    }, {
      firstName: "Bess",
      lastName: "Bessington"
    }]);

    self.openCombo = function() {
      this.optionsOpen(!this.optionsOpen());
    }.bind(this);

    self.addPerson = function() {
      self.people.push({
        firstName: "first ",
        lastName: "last "
      });
    }.bind(this);

    self.selectFromList = function(option) {
      this.value(option.firstName);
      this.optionsOpen(false);
    }.bind(this);

    self.removePerson = function() {
      self.people.remove(this);
    }.bind(this);

    self.currentFilter = ko.observable(); // property to store the filter
    self.filterProducts = ko.computed(function() {
      if (!this.currentFilter()) {
        return this.people();
      } else {
        return ko.utils.arrayFilter(this.people(), function(option) {
          return option.firstName == this.currentFilter();
        }.bind(this));
      }
    }.bind(this));

    self.filter = function() {
      this.currentFilter(this.value());
    }.bind(this);

  },
  template: '<div class="ncselect">\
    	<div class="optionlist" data-bind="visible: optionsOpen">\
    		<table>\
    			<tbody data-bind="foreach: people" >\
    				<tr data-bind="click: $parent.selectFromList" >\
    					<td data-bind="text: firstName" ></td>\
            	<td data-bind="text: lastName" > </td>\
    				</tr>\
    			</tbody>\
    		</table>\
    	</div>\
    	<div class="ipwrapper" >\
    		<input class="selectinput" type="text" data-bind="value: value" />\
        <div data-bind="click: openCombo" class="combotrigger">v</div>\
    	</div>\
    </div>'
});

function metaeditor() {
  this.keywords = ko.observableArray();
  this.add = function() {
    this.keywords.push({
  ...