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");
}
}
})