JSFiddle - React, Tailwind, and code Playground

by S_YOU

HTML

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<div ng-controller="tvCtrl">
    Number of nodes to generate: <input type="number" ng-model="numNodes" />
    <button type="button" class="btn btn-success" ng-click="refresh()"><span class="glyphicon glyphicon-refresh"></span></button>
    <button type="button" class="btn btn-success" ng-click="expandAll()">Expand All</button>
    <button type="button" class="btn btn-success" ng-click="collapseAll()">Collapse All</button>
    <jsc-tree-view id="tree" root-nodes="tree" display-property="name" />
</div>

CSS

.hasChildren{ 
    color: blue; 
    cursor: pointer;
}

JavaScript

var tvApp = angular.module("tvApp", []);

tvApp.controller("tvCtrl", ["$scope", function ($scope) {
    var buildRandomTree = function (numNodes) {
        if (numNodes < 1) return null;

        var parentId;
        var rootIdx = 1;
        var tree = [];

        var hasAnotherChild = function () {
            return Math.random() > 0.5;
        };

        var buildNode = function (parent, idx) {
            numNodes--;

            var result = {
                id: parent ? parent.id + "_" + idx : "jscTreeNd" + idx,
                name: parent ? parent.name + "." + idx : "Node: " + idx,
                children: []
            };

            var childIdx = 1;
            while (numNodes > 0 && hasAnotherChild()) {
                result.children.push(buildNode(result, childIdx));
                childIdx++;
            }

            return result;
        }

        while (numNodes > 0) {
            tree.push(buildNode(null, rootIdx));
            rootIdx++;
        }

        return tree;
    }

    $scope.refresh = function () {
        $scope.tree = buildRandomTree($scope.numNodes);
    };

    $scope.expandAll = function () {
        $scope.$broadcast("expandAll");
    };

    $scope.collapseAll = function () {
        $scope.$broadcast("collapseAll");
    }

    $scope.numNodes = 14;
    $scope.refresh();
}]);

tvApp.directive("jscTreeView", ["$compile", function ($compile) {
    var result = {
        retstrict: "E",
        scope: {
            rootNodes: "=",
            displayProperty: "@",
            isSubTree: "@",
            id: "@"
        },
        link: function (scope, element) {
            if (scope.isSubTree !== "true") {
                scope.$watch("rootNodes", function () {
                    element.children().remove();

                    var template = $compile("<ul><jsc-tree-node ng-repeat='node in rootNodes' node='node' display-property='{{ displayProperty }}' /></ul>")(scope);
                    template.on("remove",...