diff --git a/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/NestedNav.tsx b/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/NestedNav.tsx index 2c7d747ca..208c14e87 100644 --- a/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/NestedNav.tsx +++ b/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/NestedNav.tsx @@ -14,13 +14,14 @@ interface RecursiveProps { isRoot?: boolean; trace: Trace; totalMsec: number; + isExpandedAll? : boolean; } interface OpenLevels { [x: number]: boolean } -const NestedNav: FC = ({ isRoot, trace, totalMsec }) => { +const NestedNav: FC = ({ isRoot, trace, totalMsec, isExpandedAll }) => { const { isDarkTheme } = useAppState(); const { isMobile } = useDeviceDetect(); const [openLevels, setOpenLevels] = useState({} as OpenLevels); @@ -53,6 +54,26 @@ const NestedNav: FC = ({ isRoot, trace, totalMsec }) => { }); }; + const getIdsFromChildren = (tracingData: Trace) => { + const ids = [tracingData.idValue]; + tracingData?.children?.forEach((child) => { + ids.push(...getIdsFromChildren(child)); + }); + return ids; + }; + + useEffect(() => { + if (!isExpandedAll) { + setOpenLevels([]); + return; + } + + const allIds = getIdsFromChildren(trace); + const openLevels = {} as OpenLevels; + allIds.forEach(id => { openLevels[id] = true; }); + setOpenLevels(openLevels); + }, [isExpandedAll]); + return (
= ({ isRoot, trace, totalMsec }) => { )}
- {openLevels[trace.idValue] && ( + {(openLevels[trace.idValue]) && (
{hasChildren && trace.children.map((trace) => ( ))}
diff --git a/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/style.scss b/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/style.scss index 1b0d5a4c0..72bee00d9 100644 --- a/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/style.scss +++ b/app/vmui/packages/vmui/src/components/TraceQuery/NestedNav/style.scss @@ -2,6 +2,8 @@ $color-base-nested-nav: $color-tropical-blue; $color-base-nested-nav-dark: $color-background-body; +$width-line: 2px; +$left-position: calc(-1 * $padding-small); .vm-nested-nav { position: relative; @@ -48,19 +50,19 @@ $color-base-nested-nav-dark: $color-background-body; content: ""; position: absolute; top: calc(50% - 1px); - height: 2px; + height: $width-line; width: $padding-small; background-color: $color-base-nested-nav; - left: calc(-1 * $padding-small); + left: $left-position; } &:before { content: ""; position: absolute; bottom: 50%; - left: calc(-1 * $padding-small); + left: $left-position; height: calc(50% + $padding-small); - width: 2px; + width: $width-line; background-color: $color-base-nested-nav; } @@ -122,9 +124,9 @@ $color-base-nested-nav-dark: $color-background-body; content: ""; position: absolute; top: 0; - left: calc(-1 * $padding-small); + left: $left-position; height: 100%; - width: 2px; + width: $width-line; background-color: $color-base-nested-nav; } } diff --git a/app/vmui/packages/vmui/src/components/TraceQuery/TracingsView.tsx b/app/vmui/packages/vmui/src/components/TraceQuery/TracingsView.tsx index 0bf1cbdfd..ed3c377dc 100644 --- a/app/vmui/packages/vmui/src/components/TraceQuery/TracingsView.tsx +++ b/app/vmui/packages/vmui/src/components/TraceQuery/TracingsView.tsx @@ -1,7 +1,7 @@ import React, { FC, useState } from "preact/compat"; import Trace from "./Trace"; import Button from "../Main/Button/Button"; -import { CodeIcon, DeleteIcon } from "../Main/Icons"; +import { ArrowDownIcon, CodeIcon, DeleteIcon, DownloadIcon } from "../Main/Icons"; import "./style.scss"; import NestedNav from "./NestedNav/NestedNav"; import Alert from "../Main/Alert/Alert"; @@ -20,6 +20,7 @@ interface TraceViewProps { const TracingsView: FC = ({ traces, jsonEditor = false, onDeleteClick }) => { const { isMobile } = useDeviceDetect(); const [openTrace, setOpenTrace] = useState(null); + const [expandedTraces, setExpandedTraces] = useState([]); const handleCloseJson = () => { setOpenTrace(null); @@ -52,6 +53,27 @@ const TracingsView: FC = ({ traces, jsonEditor = false, onDelete setOpenTrace(tracingData); }; + const handleSaveToFile = (tracingData: Trace) => () => { + const blob = new Blob([tracingData.originalJSON], { type: "application/json" }); + const href = URL.createObjectURL(blob); + + const link = document.createElement("a"); + link.href = href; + link.download = `vmui_trace_${tracingData.queryValue}.json`; + document.body.appendChild(link); + link.click(); + + document.body.removeChild(link); + URL.revokeObjectURL(href); + }; + + const handleExpandAll = (tracingData: Trace) => () => { + setExpandedTraces(prev => prev.includes(tracingData.idValue) + ? prev.filter(n => n !== tracingData.idValue) + : [...prev, tracingData.idValue] + ); + }; + return ( <>
@@ -64,6 +86,28 @@ const TracingsView: FC = ({ traces, jsonEditor = false, onDelete

Trace for {trace.queryValue}

+ +
+ )} + onClick={handleExpandAll(trace)} + ariaLabel={expandedTraces.includes(trace.idValue) ? "Collapse All" : "Expand All"} + /> + + +