(function() { function isFolder(node) { return Array.isArray(node.nodes) && node.nodes.length > 0; } function renderNode(node, expandedPaths, depth) { const li = document.createElement("li"); li.dataset.id = node.id || ""; const row = document.createElement("div"); row.className = "row"; row.style.paddingLeft = `${(depth + 1) * 1.2}rem`; const caret = document.createElement("span"); caret.className = "caret"; const folder = isFolder(node); const expanded = expandedPaths.has(node.id) || depth === 0; caret.textContent = folder ? (expanded ? "▾" : "▸") : ""; row.appendChild(caret); const label = document.createElement("span"); label.className = "label " + (folder ? "icon-folder" : "icon-file"); label.textContent = node.text || node.id || ""; row.appendChild(label); if (node.meta) { const meta = document.createElement("span"); meta.className = "meta"; meta.textContent = node.meta; row.appendChild(meta); } li.appendChild(row); if (folder) { const ul = document.createElement("ul"); ul.className = "btv"; ul.style.display = expanded ? "block" : "none"; node.nodes.forEach(child => { ul.appendChild(renderNode(child, expandedPaths, depth + 1)); }); li.appendChild(ul); } return li; } function create(container, data, { expandedPaths = new Set(), onClick, onDblClick } = {}) { if (!container) return; container.innerHTML = ""; const ul = document.createElement("ul"); ul.className = "btv"; data.forEach(node => ul.appendChild(renderNode(node, expandedPaths, 0))); ul.addEventListener("click", evt => { const row = evt.target.closest(".row"); const li = evt.target.closest("li"); if (!li) return; const sub = li.querySelector(":scope > ul"); if (sub) { const caret = row?.querySelector(".caret") || li.querySelector(":scope > .row .caret"); const hidden = sub.style.display === "none"; sub.style.display = hidden ? "block" : "none"; if (caret) caret.textContent = hidden ? "▾" : "▸"; if (hidden) { expandedPaths.add(li.dataset.id); } else { expandedPaths.delete(li.dataset.id); } } onClick?.(li.dataset.id || ""); }); ul.addEventListener("dblclick", evt => { const li = evt.target.closest("li"); if (!li) return; const sub = li.querySelector(":scope > ul"); if (!sub) { onDblClick?.(li.dataset.id || ""); } }); container.appendChild(ul); } window.BootstrapTreeView = { create }; })();