diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html new file mode 100644 index 0000000..19ed082 --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,11 @@ + + + + Tree View + + + +
+ + + \ No newline at end of file diff --git a/src/main/webapp/script.js b/src/main/webapp/script.js new file mode 100644 index 0000000..feccb7d --- /dev/null +++ b/src/main/webapp/script.js @@ -0,0 +1,109 @@ + +const treeContainer = document.getElementById('tree-container'); + +// --- Data Generation --- +function generateRandomTree(maxDepth = 3) { + const tree = []; + const numRootItems = Math.floor(Math.random() * 5) + 3; // 3 to 7 root items + + for (let i = 0; i < numRootItems; i++) { + tree.push(generateRandomItem(1, maxDepth)); + } + + return tree; +} + +function generateRandomItem(currentDepth, maxDepth) { + const item = { + label: `Item ${currentDepth}-${Math.random().toString(36).substring(2, 15)} ${'Looooooooong '.repeat(Math.floor(Math.random() * 5))}`, + active: Math.random() < 0.5, + }; + + if (currentDepth < maxDepth && Math.random() < 0.7) { // 70% chance of having children + item.children = []; + const numChildren = Math.floor(Math.random() * 4) + 1; // 1 to 4 children + for (let i = 0; i < numChildren; i++) { + item.children.push(generateRandomItem(currentDepth + 1, maxDepth)); + } + } + + return item; +} + +// --- Rendering --- +function renderTree(data, parentElement) { + const treeElement = document.createElement('div'); + treeElement.classList.add('tree'); + parentElement.appendChild(treeElement); + + data.forEach(item => { + renderItem(item, treeElement); + }); +} + +function renderItem(item, parentElement) { + const itemElement = document.createElement('div'); + itemElement.classList.add('tree-item'); + if (item.active) { + itemElement.classList.add('active'); + } + + const expandButton = document.createElement('div'); + expandButton.classList.add('expand-button'); + if (item.children) { + expandButton.textContent = '+'; + } + itemElement.appendChild(expandButton); + + const label = document.createElement('div'); + label.classList.add('tree-label'); + label.textContent = item.label; + itemElement.appendChild(label); + + const activateButton = document.createElement('button'); + activateButton.classList.add('activate-button'); + activateButton.textContent = item.active ? 'Deactivate' : 'Activate'; + itemElement.appendChild(activateButton); + + parentElement.appendChild(itemElement); + + if (item.children) { + const childrenContainer = document.createElement('div'); + childrenContainer.classList.add('tree-item-children'); + childrenContainer.style.display = 'none'; // Initially hidden + parentElement.appendChild(childrenContainer); + + item.children.forEach(child => { + renderItem(child, childrenContainer); + }); + } +} + +// --- Event Listeners --- +treeContainer.addEventListener('click', function(event) { + const target = event.target; + + if (target.classList.contains('expand-button') && target.textContent) { + const childrenContainer = target.parentElement.nextElementSibling; + if (childrenContainer && childrenContainer.classList.contains('tree-item-children')) { + if (childrenContainer.style.display === 'none') { + childrenContainer.style.display = 'block'; + target.textContent = '-'; + } else { + childrenContainer.style.display = 'none'; + target.textContent = '+'; + } + } + } + + if (target.classList.contains('activate-button')) { + const itemElement = target.parentElement; + itemElement.classList.toggle('active'); + const isActive = itemElement.classList.contains('active'); + target.textContent = isActive ? 'Deactivate' : 'Activate'; + } +}); + +// --- Initial Load --- +const randomTreeData = generateRandomTree(); +renderTree(randomTreeData, treeContainer); diff --git a/src/main/webapp/style.css b/src/main/webapp/style.css new file mode 100644 index 0000000..8f5fb1c --- /dev/null +++ b/src/main/webapp/style.css @@ -0,0 +1,62 @@ +#tree-container { + max-width: 500px; + overflow-x: auto; + white-space: nowrap; + border: 1px solid #ccc; + padding: 10px; +} + +.tree { + min-width: 100%; + display: inline-block; +} + +.tree-item { + display: flex; + align-items: center; + padding: 5px; +} + +.tree-item.active { + background-color: #e0e0e0; +} + +.tree-item-children { + padding-left: 20px; +} + +.expand-button { + cursor: pointer; + margin-right: 5px; + width: 16px; + height: 16px; + text-align: center; + line-height: 16px; + border: 1px solid #ccc; + background-color: #f0f0f0; + flex-shrink: 0; +} + +.expand-button:empty { + border: none; + background-color: transparent; + cursor: default; +} + +.activate-button { + margin-left: auto; + flex-shrink: 0; + position: sticky; + right: 0; + background-color: white; /* Default background */ + padding-left: 5px; /* Add some space from the label */ + padding-right: 5px; +} + +.tree-item.active .activate-button { + background-color: #e0e0e0; /* Match active item background */ +} + +.tree-label { + /* No flex-grow to prevent pushing the button out */ +} \ No newline at end of file