JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://code.jquery.com/jquery-1.9.0.js"></script>
<script src="http://code.angularjs.org/1.2.0/angular.js"></script>
<div ng-app="treeControl">
<div ng-controller="Controller">
    <div style="display: flex; flex-direction: row">
        <div class="tree-border">
            <h3>Classic Tree</h3>
            <div>Selected: {{node1.label?node1.label:"N/A"}}</div>
            <treecontrol class="tree-classic" tree-model="treedata" node-children="children" on-selection="showSelected(node)" selected-node="node1">
                label: {{node.label}} ({{node.id}})
            </treecontrol>
        </div>
        <div class="tree-border">
            <h3>Light Tree</h3>
            <div>Selected: {{node2.label?node2.label:"N/A"}}</div>
            <treecontrol class="tree-light" tree-model="treedata" node-children="children" on-selection="showSelected(node)" selected-node="node2">
                label: {{node.label}} ({{node.id}})
            </treecontrol>
        </div>
        <div class="tree-border" style=" background: #555;">
            <h3 style="color: white">Dark Tree</h3>
            <div style="color: white">Selected: {{node3.label?node2.label:"N/A"}}</div>
            <treecontrol class="tree-dark" tree-model="treedata" node-children="children" on-selection="showSelected(node)" selected-node="node3">
                label: {{node.label}} ({{node.id}})
            </treecontrol>
        </div>
    </div>
    <h2>Selection</h2>
    Based on callback (on-selection): {{selected}}</br>
    Based on binding (selected-node): {{node1.label}}, {{node2.label}}, {{node3.label}}
    <h2>Mutate the tree</h2>
    <a class="fake-link" ng-click="addRoot()">add root</a></br>
    <a class="fake-link" ng-click="addChild()">add child (to the first root of the tree)</a></br>
    <script>

        function Controller($scope) {

            var num = 1;
            function getNum() {
                return num++;
            }

           ...

CSS

/* general tree css */

treecontrol {
    /* prevent user selection */
    -moz-user-select: -moz-none;
    -khtml-user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;

    /* default */
    font-family: Verdana, Helvetica, Arial, sans-serif;
    font-size:13px;
    color: #555;
    text-decoration: none;
}

treecontrol ul {
    margin: 0;
    padding: 0;
    list-style: none;
    border: none;
    overflow: hidden;
}

treecontrol li {
    position: relative;
    padding: 0 0 0 20px;
    line-height: 20px;
}

treecontrol li.tree-expanded i.tree-normal, treecontrol li.tree-collapsed i.tree-normal {display:none;}
treecontrol li.tree-expanded i.tree-has-children, treecontrol li.tree-collapsed i.tree-has-children {display:inline;}
treecontrol li.tree-normal i.tree-has-children {display:none;}
treecontrol li.tree-normal i.tree-normal {display:inline;}

treecontrol li i {
    cursor: pointer;
}

treecontrol li .tree-label {
    cursor: pointer;
    display: inline;
}

/* classic tree css */

treecontrol.tree-classic li.tree-expanded i {
    padding: 1px 10px;
    background: url("https://raw.github.com/wix/angular.treecontrol/master/images/folder.png") no-repeat;
}

treecontrol.tree-classic li.tree-collapsed i {
    padding: 1px 10px;
    background: url("https://raw.github.com/wix/angular.treecontrol/master/images/folder-closed.png") no-repeat;
}

treecontrol.tree-classic li.tree-normal i {
    padding: 1px 10px;
    background: url("https://raw.github.com/wix/angular.treecontrol/master/images/file.png") no-repeat;
}

treecontrol.tree-classic li .tree-selected {
    background-color: #aaddff;
    font-weight: bold;
}

/* light tree css */

treecontrol.tree-light li.tree-expanded i {
    padding: 1px 10px;
    background: url("https://raw.github.com/wix/angular.treecontrol/master/images/node-opened-2.png") no-repeat;
}

treecontrol.tree-light li.tree-collapsed i {
    padding: 1px 10px;
    background:...

JavaScript

(function ( angular ) {
    'use strict';

    angular.module( 'treeControl', [] )
        .directive( 'treecontrol', ['$compile', function( $compile ) {
            return {
                restrict: 'E',
                require: "treecontrol",
                transclude: true,
                scope: {
                    treeModel: "=",
                    selectedNode: "=",
                    onSelection: "&",
                    nodeChildren: "@"
                },
                controller: function( $scope ) {

                    $scope.nodeChildren = $scope.nodeChildren || 'children';
                    $scope.expandedNodes = {};

                    $scope.headClass = function(node) {
                        if (node[$scope.nodeChildren].length && !$scope.expandedNodes[this.$id])
                            return "tree-collapsed";
                        else if (node[$scope.nodeChildren].length && $scope.expandedNodes[this.$id])
                            return "tree-expanded";
                        else
                            return "tree-normal"
                    };

                    $scope.nodeExpanded = function() {
                        return $scope.expandedNodes[this.$id];
                    };

                    $scope.selectNodeHead = function() {
                        $scope.expandedNodes[this.$id] = !$scope.expandedNodes[this.$id];
                    };

                    $scope.selectNodeLabel = function( selectedNode ){
                        $scope.selectedScope = this.$id;
                        $scope.selectedNode = selectedNode;
                        if ($scope.onSelection)
                            $scope.onSelection({node: selectedNode});
                    };

                    $scope.selectedClass = function() {
                        return (this.$id == $scope.selectedScope)?"tree-selected":"";
                    };

                    //tree template
                    var template =
      ...