* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
width: 100%;
max-width: 800px;
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 28px;
font-weight: 700;
margin-bottom: 8px;
}
.header p {
font-size: 14px;
opacity: 0.9;
}
.controls {
padding: 24px;
background: #f8f9fa;
border-bottom: 1px solid #e9ecef;
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);
color: white;
box-shadow: 0 4px 12px rgba(238, 9, 121, 0.3);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(238, 9, 121, 0.4);
}
.btn-secondary {
background: white;
color: #ee0979;
border: 2px solid #ee0979;
}
.btn-secondary:hover {
background: #ee0979;
color: white;
}
.tree-container {
padding: 24px;
max-height: 500px;
overflow-y: auto;
}
.tree-container::-webkit-scrollbar {
width: 6px;
}
.tree-container::-webkit-scrollbar-track {
background: #f1f1f1;
}
.tree-container::-webkit-scrollbar-thumb {
background: #ee0979;
border-radius: 3px;
}
.tree-node {
margin-left: 24px;
}
.tree-node:first-child {
margin-left: 0;
}
.node-content {
display: flex;
align-items: center;
padding: 10px 12px;
margin: 4px 0;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
user-select: none;
}
.node-content:hover {
background: linear-gradient(135deg, rgba(238, 9, 121, 0.1) 0%, rgba(255, 106, 0, 0.1) 100%);
}
.node-content.selected {
background: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);
color: white;
}
.toggle-icon {
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
margin-right: 8px;
font-size: 12px;
transition: transform 0.3s;
}
.toggle-icon.expanded {
transform: rotate(90deg);
}
.node-icon {
margin-right: 8px;
font-size: 18px;
}
.node-label {
flex: 1;
font-size: 15px;
font-weight: 500;
}
.node-badge {
background: rgba(238, 9, 121, 0.15);
color: #ee0979;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
margin-left: 8px;
}
.node-content.selected .node-badge {
background: rgba(255, 255, 255, 0.3);
color: white;
}
.children {
display: none;
animation: slideDown 0.3s ease;
}
.children.show {
display: block;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #6c757d;
}
.empty-state-icon {
font-size: 48px;
margin-bottom: 16px;
}
.empty-state p {
font-size: 16px;
}
.add-node-form {
display: none;
padding: 20px;
background: #f8f9fa;
border-radius: 12px;
margin: 16px 0;
}
.add-node-form.show {
display: block;
animation: slideDown 0.3s ease;
}
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
font-size: 14px;
font-weight: 600;
color: #2d3748;
margin-bottom: 6px;
}
.form-group input {
width: 100%;
padding: 10px 12px;
border: 2px solid #e9ecef;
border-radius: 8px;
font-size: 14px;
transition: all 0.3s;
}
.form-group input:focus {
outline: none;
border-color: #ee0979;
}
.form-actions {
display: flex;
gap: 10px;
}
let treeData = [];
let selectedNode = null;
let nodeIdCounter = 1;
// Initialize with sample data
function initTree() {
treeData = [
{
id: nodeIdCounter++,
label: 'Documents',
icon: '📁',
children: [
{
id: nodeIdCounter++,
label: 'Projects',
icon: '📂',
children: [
{ id: nodeIdCounter++, label: 'Project A', icon: '📄', children: [] },
{ id: nodeIdCounter++, label: 'Project B', icon: '📄', children: [] }
]
},
{ id: nodeIdCounter++, label: 'Reports', icon: '📊', children: [] }
]
},
{
id: nodeIdCounter++,
label: 'Images',
icon: '🖼️',
children: [
{ id: nodeIdCounter++, label: 'Vacation', icon: '🏖️', children: [] },
{ id: nodeIdCounter++, label: 'Work', icon: '💼', children: [] }
]
},
{
id: nodeIdCounter++,
label: 'Videos',
icon: '🎬',
children: []
}
];
renderTree();
}
function renderTree() {
const container = document.getElementById('treeContainer');
if (treeData.length === 0) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">🌳</div>
<p>No nodes yet. Add a root node to get started!</p>
</div>
`;
return;
}
container.innerHTML = '';
treeData.forEach(node => {
container.appendChild(createTreeNode(node));
});
}
function createTreeNode(node, level = 0) {
const nodeDiv = document.createElement('div');
nodeDiv.className = 'tree-node';
const hasChildren = node.children && node.children.length > 0;
const contentDiv = document.createElement('div');
contentDiv.className = 'node-content';
contentDiv.onclick = (e) => {
e.stopPropagation();
selectNode(node, contentDiv);
};
// Toggle icon
const toggleIcon = document.createElement('span');
toggleIcon.className = 'toggle-icon';
toggleIcon.innerHTML = hasChildren ? '▶' : '○';
if (hasChildren) {
toggleIcon.onclick = (e) => {
e.stopPropagation();
toggleNode(toggleIcon, childrenDiv);
};
}
// Node icon
const nodeIcon = document.createElement('span');
nodeIcon.className = 'node-icon';
nodeIcon.textContent = node.icon || '📄';
// Node label
const nodeLabel = document.createElement('span');
nodeLabel.className = 'node-label';
nodeLabel.textContent = node.label;
// Badge for child count
if (hasChildren) {
const badge = document.createElement('span');
badge.className = 'node-badge';
badge.textContent = node.children.length;
nodeLabel.appendChild(badge);
}
contentDiv.appendChild(toggleIcon);
contentDiv.appendChild(nodeIcon);
contentDiv.appendChild(nodeLabel);
// Add child button
const addChildBtn = document.createElement('span');
addChildBtn.innerHTML = '➕';
addChildBtn.style.cursor = 'pointer';
addChildBtn.style.marginLeft = 'auto';
addChildBtn.onclick = (e) => {
e.stopPropagation();
addChildNode(node);
};
contentDiv.appendChild(addChildBtn);
nodeDiv.appendChild(contentDiv);
// Children container
if (hasChildren) {
const childrenDiv = document.createElement('div');
childrenDiv.className = 'children';
node.children.forEach(child => {
childrenDiv.appendChild(createTreeNode(child, level + 1));
});
nodeDiv.appendChild(childrenDiv);
}
return nodeDiv;
}
function toggleNode(toggleIcon, childrenDiv) {
if (childrenDiv) {
childrenDiv.classList.toggle('show');
toggleIcon.classList.toggle('expanded');
}
}
function selectNode(node, element) {
// Remove previous selection
document.querySelectorAll('.node-content').forEach(el => {
el.classList.remove('selected');
});
element.classList.add('selected');
selectedNode = node;
}
function toggleAddForm() {
const form = document.getElementById('addNodeForm');
form.classList.toggle('show');
if (form.classList.contains('show')) {
document.getElementById('nodeName').focus();
}
}
function addNode() {
const name = document.getElementById('nodeName').value.trim();
if (!name) {
alert('Please enter a node name');
return;
}
const newNode = {
id: nodeIdCounter++,
label: name,
icon: '📄',
children: []
};
treeData.push(newNode);
document.getElementById('nodeName').value = '';
toggleAddForm();
renderTree();
}
function addChildNode(parentNode) {
const name = prompt('Enter child node name:');
if (!name) return;
const newNode = {
id: nodeIdCounter++,
label: name,
icon: '📄',
children: []
};
parentNode.children.push(newNode);
renderTree();
}
function cancelAdd() {
document.getElementById('nodeName').value = '';
toggleAddForm();
}
function expandAll() {
document.querySelectorAll('.children').forEach(el => {
el.classList.add('show');
});
document.querySelectorAll('.toggle-icon').forEach(el => {
if (el.innerHTML === '▶') {
el.classList.add('expanded');
}
});
}
function collapseAll() {
document.querySelectorAll('.children').forEach(el => {
el.classList.remove('show');
});
document.querySelectorAll('.toggle-icon').forEach(el => {
el.classList.remove('expanded');
});
}
function clearTree() {
if (confirm('Are you sure you want to clear the entire tree?')) {
treeData = [];
selectedNode = null;
renderTree();
}
}
// Initialize tree on page load
initTree();
No comments yet. Be the first!