JSFiddle - React, Tailwind, and code Playground

HTML

<div ng-app="app" ng-cloak>
    <div ng-controller="TreeController">
        <div class="new-item">
            <a class="btn add icon" ng-click="addChild(data)">New Item</a>
        </div>
        <div class="navi-live">
            <ol ui-nested-sortable="{
        listType: 'ol',
        items: 'li',
        doNotClear: true,
        placeholder: 'ui-state-highlight',
        forcePlaceholderSize: true,
        toleranceElement: '> div'
      }" ui-nested-sortable-stop="update($event, $ui)" class="navi-item-container">
                <li ya-tree="child in data.children at ol" ng-class="{minimized:child.minimized}" class="navi-item" ng-style="getStyle(child)">
                    <div class="nav-item-contents">
                      <div class="toggle-container">
                          <a ng-show="child.children.length"  class="toggle btn small" ng-click="toggleMinimized(child)" ng-switch on="child.minimized"><span ng-switch-when="true">&#x25B6;</span><span ng-switch-default>&#x25BC;</span>
                          </a>
                      </div>
                        <span class="nav-item-main">
                        <input ng-model="child.title" />
                          <a class="btn small" ng-click="addChild(child)">add a child</a>
                          <a class="btn small" ng-click="remove(child)">delete</a>
                        </span>
                    </div>
                    <ol ng-class="{pregnant:child.children.length}" class="navi-item-container"></ol>
                </li>
            </ol>
        </div>

CSS

.new-item {
    margin:10px;
    text-align: right;
}
.ui-state-highlight {
    background-color:rgba(0, 0, 0, 0.1);
}
.nav-item-main {
    padding-left:30px;
}
.navi-live {
    -webkit-border-radius: 3px;
    border-radius: 3px;
    background-clip: padding-box;
    -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 2px 10px -2px rgba(0, 0, 0, 0.1);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 2px 10px -2px rgba(0, 0, 0, 0.1);
}
.navi-live ol {
    margin:0;
    padding:0;
    list-style-type: none;
}
.navi-live li {
    margin:0;
    padding:0px;
    list-style-type: none;
}
.navi-live ol .nav-item-contents {
    padding-left:20px;
}
.navi-live ol ol .nav-item-contents {
    padding-left:40px;
}
.navi-live ol ol ol .nav-item-contents {
    padding-left:60px;
}
.navi-live ol ol ol ol .nav-item-contents {
    padding-left:80px;
}
.navi-live ol ol ol ol ol .nav-item-contents {
    padding-left:100px;
}
.navi-live .nav-item-contents {
    padding-top:7px;
    padding-bottom:7px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.navi-live .toggle-container {
    border:none;
    width:50px;
    display:inline-block;
    padding-left:10px;
}
.navi-live .nav-item > .toggle-container {
    padding-left:20px;
}
.minimized > ol > li {
    display:none;
}
.minimized > ol {
    border: 0 none transparent;
}

JavaScript

// http://jsfiddle.net/michieljoris/VmtfR/


var app = angular.module('app', []);

app.directive('yaTree', function () {

    return {
        restrict: 'A',
        transclude: 'element',
        priority: 1000,
        terminal: true,
        compile: function (tElement, tAttrs, transclude) {

            var repeatExpr, childExpr, rootExpr, childrenExpr;

            repeatExpr = tAttrs.yaTree.match(/^(.*) in ((?:.*\.)?(.*)) at (.*)$/);
            childExpr = repeatExpr[1];
            rootExpr = repeatExpr[2];
            childrenExpr = repeatExpr[3];
            branchExpr = repeatExpr[4];

            return function link(scope, element, attrs) {

                var rootElement = element[0].parentNode,
                    cache = [];

                // Reverse lookup object to avoid re-rendering elements
                function lookup(child) {
                    var i = cache.length;
                    while (i--) {
                        if (cache[i].scope[childExpr] === child) {
                            return cache.splice(i, 1)[0];
                        }
                    }
                }

                scope.$watch(rootExpr, function (root) {

                    var currentCache = [];

                    // Recurse the data structure
                    (function walk(children, parentNode, parentScope, depth) {

                        var i = 0,
                            n = children.length,
                            last = n - 1,
                            cursor,
                            child,
                            cached,
                            childScope,
                            grandchildren;

                        // Iterate the children at the current level
                        for (; i < n; ++i) {

                            // We will compare the cached element to the element in 
                            // at the destination index. If it does not match, then 
                           ...