JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<button data-bind='click:AddRootNode'>Add New Root Node</button>
<button data-bind='click:AddChildNode,visible: tree.selectedNode'>Add Child toSelected Node</button>
<button data-bind='click:RemoveNode,visible: tree.selectedNode'>Delete Selected Node</button>

<div class='tree'  data-bind='with: tree'>
  <ul data-bind='template: {name:"treeNode", foreach: children}'>
  </ul>    
</div>  

<div data-bind='with: tree.selectedNode'>
    <!-- ko with: data -->
        <input data-bind='value:name'>
    <!-- /ko   -->
</div>    


<script id='treeNode' type='text/html'>
  <li data-bind='css:{closed:isClosed,open:isOpen, leaf: isLeaf, last: isLast}, click: toggleOpen, clickBubble: false'> 
    <ins></ins>
    <span class='caption' data-bind='text:caption, css: {selected:isSelected},click:toggleSelection, clickBubble: false'></span>
    <ul data-bind='template: {name:"treeNode", foreach: children}'>
    </ul> 
  </li>  
 </script>

CSS

.tree li, 
.tree ins{ background-image:url("http://habrastorage.org/storage2/0eb/507/98d/0eb50798dca00f5cc8e153e6da9a87f9.png"); background-repeat:no-repeat; background-color:transparent; }
.tree li { background-position:-90px 0; background-repeat:repeat-y; }

.tree li { display:block; min-height:18px; line-height:18px; white-space:nowrap; margin-left:18px; min-width:18px; }

.tree ul, .tree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; }
.tree li { display:block; min-height:18px; line-height:18px; white-space:nowrap; margin-left:18px; min-width:18px; } 
.tree > ul > li { margin-left:0px; }

.tree li.last { background:transparent; }
.tree .open > ins { background-position:-72px 0;}
.tree .closed > ins { background-position:-54px 0;}
.tree .leaf > ins { background-position:-36px 0;}


.tree ins { display:inline-block; text-decoration:none; width:18px; height:18px; margin:0 0 0 0; padding:0; }
li.open > ul { display:block; }
li.closed > ul { display:none; } 
.selected {background-color: #ccc; }
span.caption {cursor: pointer}

JavaScript

function TreeView(data, propMap){
  var treeView = this;
    
  this.data = data;  
  this.selectedNode = ko.observable();  
  this.children = ko.computed(function(){ 
       return dataToNodes(ko.utils.unwrapObservable(data));
  });                                       
  setIsLast(this.children());
  this.children.subscribe(function(newVal){
          setIsLast(newVal);
  });   
  
  

  function dataToNodes(dataArray,old){
      var res = [];
    for(var i=0,l=dataArray.length;i<l;i++){
        res.push(dataArray[i]._treeviewNode || new TreeViewNode(dataArray[i]));
    }
    return res;
  }
    
  function setIsLast(children){
   for(var i=0,l=children.length;i<l;i++){
     children[i].isLast(i==(l-1));   
   }
  }

  function TreeViewNode(data){
   var self = this;
   this.data = data;  
   data._treeviewNode = this;   
   var 
       map = (typeof propMap == 'function') ? propMap(data):propMap;
       captionProp = (map && map.caption)||'caption',
       childrenProp = (map && map.children)||'children';
   this.caption = data[captionProp];
   if(data[childrenProp]) this.children = ko.computed(function(){
         return dataToNodes(ko.utils.unwrapObservable(data[childrenProp]));
    });
    else this.children = null; 
   
   this.isOpen = ko.observable();
   this.isClosed = ko.computed(function(){
        return !this.isOpen();
   },this);
  
   this.isLeaf = ko.computed(function(){
         return !(this.children && this.children().length);
   },this);
   this.isLast = ko.observable(false);
      
   if(this.children){
       setIsLast(this.children());
       this.children.subscribe(function(newVal){
          setIsLast(newVal);
       });   
   }
      
   this.toggleOpen = function(){
     self.isOpen(!self.isOpen());
   };
   
   this.isSelected = ko.computed(function(){
         return (treeView.selectedNode()===this)    
   },this);   
   
   this.toggleSelection = function(){
       if(this.isSelected()) treeView.selectedNode(null);
       else...