diff --git a/scripts/logviewer/index.html b/scripts/logviewer/index.html index aed67512..2245e7a8 100644 --- a/scripts/logviewer/index.html +++ b/scripts/logviewer/index.html @@ -72,6 +72,31 @@ grid-area: nav; } + .timeline li:not(.expanded) > ol { + display: none; + } + + .timeline li > div { + display: flex; + } + + .timeline .toggleExpanded { + border: none; + background: none; + width: 24px; + height: 24px; + margin-right: 4px; + cursor: pointer; + } + + .timeline .toggleExpanded:before { + content: "▶"; + } + + .timeline li.expanded > div > .toggleExpanded:before { + content: "▼"; + } + .timeline ol { list-style: none; padding: 0 0 0 20px; @@ -86,6 +111,13 @@ padding: 2px; display: flex; margin: 1px; + flex: 1; + cursor: pointer; + } + + + .timeline div.item:not(.has-children) { + margin-left: calc(24px + 4px + 1px); } .timeline div.item .caption { diff --git a/scripts/logviewer/main.js b/scripts/logviewer/main.js index 2a805787..5796e6f4 100644 --- a/scripts/logviewer/main.js +++ b/scripts/logviewer/main.js @@ -13,21 +13,26 @@ main.addEventListener("click", event => { selectedItemNode.classList.remove("selected"); selectedItemNode = null; } - const itemNode = event.target.closest(".item"); - if (itemNode) { - selectedItemNode = itemNode; - selectedItemNode.classList.add("selected"); - const path = selectedItemNode.dataset.path; - let item = rootItem; - let parent; - if (path.length) { - const indices = path.split("/").map(i => parseInt(i, 10)); - for(const i of indices) { - parent = item; - item = itemChildren(item)[i]; + if (event.target.classList.contains("toggleExpanded")) { + const li = event.target.parentElement.parentElement; + li.classList.toggle("expanded"); + } else { + const itemNode = event.target.closest(".item"); + if (itemNode) { + selectedItemNode = itemNode; + selectedItemNode.classList.add("selected"); + const path = selectedItemNode.dataset.path; + let item = rootItem; + let parent; + if (path.length) { + const indices = path.split("/").map(i => parseInt(i, 10)); + for(const i of indices) { + parent = item; + item = itemChildren(item)[i]; + } } + showItemDetails(item, parent); } - showItemDetails(item, parent); } }); @@ -96,16 +101,22 @@ function normalizeValueKey(key) { // returns the node and the total range (recursively) occupied by the node function itemToNode(item, path) { + const hasChildren = !!itemChildren(item)?.length; const className = { item: true, + "has-children": hasChildren, error: itemError(item), [`type-${itemType(item)}`]: !!itemType(item), [`level-${itemLevel(item)}`]: true, }; + const li = t.li([ - t.div({className, "data-path": path.join("/")}, [ - t.span({class: "caption"}, itemCaption(item)), - t.span({class: "duration"}, `(${itemDuration(item)}ms)`), + t.div([ + hasChildren ? t.button({className: "toggleExpanded"}) : "", + t.div({className, "data-path": path.join("/")}, [ + t.span({class: "caption"}, itemCaption(item)), + t.span({class: "duration"}, `(${itemDuration(item)}ms)`), + ]) ]) ]); if (itemChildren(item) && itemChildren(item).length) {