JSFiddle - React, Tailwind, and code Playground

by Arsen

HTML

<div class='tree-node'>

</div>
<script src="//onlinegdb.com/embed/js/xbaSc5BHU?theme=light"></script>

CSS

.tree-node ul {
	list-style: none;
}

.tree-node ul p {
  cursor: pointer;
}

.tree-node ul p::before {
	content: " \25BC \0020";
}

.tree-node ul p.collapsed::before {
	content: " \25BA \0020";
}

.tree-node ul p.leaf {
  cursor: inherit;
}

.tree-node ul p.leaf::before {
	content: "";
}

JavaScript

const data = [
        {id: 1, parent_id: null, name: 'Lang'},
        {id: 2, parent_id: null, name: 'DB'},
        {id: 4, parent_id: null, name: 'Ololo'},

        {id: 5, parent_id: 1, name: 'C'},
        {id: 6, parent_id: 1, name: 'C++'},
        {id: 7, parent_id: 1, name: 'Go'},
        {id: 8, parent_id: 1, name: 'Python'},
        {id: 9, parent_id: 1, name: 'JS'},

        {id: 10, parent_id: 8, name: '2.7'},
        {id: 11, parent_id: 8, name: '3'},

        {id: 12, parent_id: 2, name: 'Postgres'},
        {id: 13, parent_id: 2, name: 'Oracle'},
        {id: 14, parent_id: 2, name: 'MySQL'},

        {id: 15, parent_id: 12, name: '9'},
        {id: 16, parent_id: 15, name: '9.1'},
        {id: 17, parent_id: 15, name: '9.2'},
        {id: 18, parent_id: 15, name: '9.3'},


        {id: 32, parent_id: 12, name: '12.1'},

        {id: 40, parent_id: 17, name: '9.2.1'},
        {id: 41, parent_id: 17, name: '9.2.2'},
        {id: 42, parent_id: 17, name: '9.2.3'},


    ],
    treeDOM = document.querySelector('.tree-node'),
    generateTree = (data) => {
        let m = {
                root: {id:null, parent_id: null, name: '*', children: []},
            };
        
        for (let i=0; i<data.length; i++) {
            data[i].children = [];
            m[data[i].id] = data[i];
        }
        for (let i=0; i<data.length; i++) {
            m[data[i].parent_id || 'root'].children.push(data[i]);
        }

        return m.root;
    },
    expandUL = (p, ul) => () => {
        ul.style.display = ul.style.display==='none' ? '' : 'none';
        p.className = p.className==='collapsed' ? '' : 'collapsed';
    },
    generateDOM = (root) => {
        const container = document.createElement('div'),
            p =  document.createElement('p'),
            ul =  document.createElement('ul');

        p.innerText = root.name;
        if (root.children.length>0) {
            p.onclick = expandUL(p, ul);
        } else {
            p.className =...