JSFiddle - React, Tailwind, and code Playground

by Lazaro Contreras

HTML

<tree>
  <branch name="Folder 1">
    <branch name="Folder 2">
      <branch name="Folder 3">
        <branch name="Folder 4">
          <leaf name="Archivo 0"></leaf>
        </branch>
        <branch name="Folder 5">
          <leaf name="Archivo 1"></leaf>
        </branch>
      </branch>
      <leaf name="Archivo 2"></leaf>
    </branch>
    <leaf name="Archivo 3"></leaf>
    <leaf name="Archivo 4"></leaf>
    <leaf name="Archivo 5"></leaf>
  </branch>
</tree>

CSS

tree {
  width: 200px;
  overflow: hidden;
  display: block;
}

branch::before,
leaf::before {
  content: attr(name);
  user-select: none;
}

branch::before,
leaf::before {
  content: attr(name);
  user-select: none;
}

leaf {
  padding-left: 22px;
}

branch {
  overflow: hidden;
  height: 17px;
  transition: all 0.3s;
  padding-left: 10px;
}

branch,
leaf {
  display: block;
  position: relative;
  left: 0;
}

branch.active {
  height: max-content;
}

tree:hover branch line {
  border-left: 1px gray dashed;
  bottom: 0;
  position: absolute;
  left: 12px;
  top: 17px;
}

tree branch line.active {
  border-left: 2px DodgerBlue solid;
  bottom: 0;
  position: absolute;
  left: 12px;
  top: 17px;
  z-index: -1;
  
  width: 200px;
}

branch area{
  position: absolute;
  top: 0;
  left: 2px;
  right: 0;
  height: 17px;
  display: block;
}

branch area:hover{
  background: #0003;
}

arrow {
  display: inline-block;
  position: relative;
  user-select: none;
  left: -4px;
  height: 16px;
  width: 16px;
}

area polyline{
  fill:white;
  stroke:#666;
  stroke-width:2;
}

area path{
  fill:#666;
}

area path.m{
  fill:#666;
}

area.active:hover path.m{
  fill:DodgerBlue;
}

area:hover path{
  fill:DodgerBlue;
}

area.active:hover polyline, area:hover polyline{
  stroke:DodgerBlue;
}


area.active path{
  fill:none;
}

area.active path.m{
  fill:#666;
}

area:active polyline{
  fill:#666;
  stroke:#666 !important;
}

area.active:active path{
  fill:none;
}

area.active:active path.m{
  fill:white;
}

area:active path{
  fill:white;
}

JavaScript

var branches = document.querySelectorAll('branch');

branches.forEach((b) => {

  let arrow = document.createElement("arrow");
  let line = document.createElement("line");
  let clickArea = document.createElement("area");
  clickArea.innerHTML = '<svg class="opBox" height="16" width="16" viewBox="0 0 16 16" onclick="this.classList.toggle(\'active\')"><polyline points="1,1 1,15 15,15 15,1 1,1" class="cuadro" /><path d="M7 3 L9 3 L9 7 L13 7 L13 9 L9 9 L9 13 L7 13 L7 9 L3 9 L3 7 L7 7 Z" /><path class="m" d="M3 7 L13 7 L13 9 L3 9 Z" /></svg>';
  b.insertBefore(arrow, b.firstChild);
  b.appendChild(clickArea);
  b.appendChild(line);



  clickArea.onclick = () => {
    b.classList.toggle("active");
    arrow.classList.toggle("active");

    document.querySelectorAll('line').forEach((l) => {
      if (!line.classList.contains('active')) {
        l.classList.remove("active");
      }
    });

    console.log(line.classList.contains('active'), line.className);

    if (!line.classList.contains('active') && !line.classList.contains('open')) {
      line.classList.add("active");
    } else {
      line.classList.remove("active");
      b.parentNode.lastChild.classList.toggle("active");
    }

    if (!line.classList.contains('open')) {
      line.classList.add("open");
    } else {
      line.classList.remove("open");
    }
  }
})



var leaves = document.querySelectorAll('leaf');

leaves.forEach((l) => {
  let clickArea = document.createElement("area");
  l.appendChild(clickArea);
  clickArea.onclick = () => {
    var line = l.parentNode.lastChild;
    document.querySelectorAll('line').forEach((l) => {
      if (!line.classList.contains('active')) {
        l.classList.remove("active");
      }
    });
    if (!line.classList.contains('active')) {
      line.classList.add("active");
    }
  }
})