diff --git a/log-viewer/modules/analysis-view/AnalysisView.ts b/log-viewer/modules/analysis-view/AnalysisView.ts index 0f53c76b..4435a9f0 100644 --- a/log-viewer/modules/analysis-view/AnalysisView.ts +++ b/log-viewer/modules/analysis-view/AnalysisView.ts @@ -14,23 +14,23 @@ import { RootNode, TimedNode } from '../parsers/TreeParser'; import { hostService } from '../services/VSCodeService'; let analysisTable: Tabulator; -let tableContainer: HTMLDivElement; +let tableContainer: HTMLDivElement | null; @customElement('analysis-view') export class AnalysisView extends LitElement { @property() timelineRoot: RootNode | null = null; + get _tableWrapper(): HTMLDivElement | null { + return (tableContainer = this.renderRoot?.querySelector('#analysis-table') ?? null); + } + constructor() { super(); } updated(changedProperties: PropertyValues): void { - const timlineRoot = changedProperties.has('timelineRoot'); - if (this.timelineRoot && timlineRoot) { - tableContainer = this.shadowRoot?.getElementById('analysis-table') as HTMLDivElement; - if (tableContainer) { - initAnalysisRender(tableContainer, this.timelineRoot); - } + if (this.timelineRoot && changedProperties.has('timelineRoot')) { + this._appendTableWhenVisible(); } } @@ -72,22 +72,27 @@ export class AnalysisView extends LitElement { const checkBox = event.target as HTMLInputElement; analysisTable.setGroupBy(checkBox.checked ? 'type' : ''); } -} -export function initAnalysisRender(analysisRoot: HTMLElement, rootMethod: RootNode) { - if (analysisRoot) { - const analysisObserver = new IntersectionObserver((entries, observer) => { - const visible = entries[0].isIntersecting; - if (visible) { - renderAnalysis(rootMethod); - observer.disconnect(); - } - }); - analysisObserver.observe(analysisRoot); + _appendTableWhenVisible() { + const rootMethod = this.timelineRoot; + const tableWrapper = this._tableWrapper; + if (tableWrapper && rootMethod) { + const analysisObserver = new IntersectionObserver((entries, observer) => { + const visible = entries[0].isIntersecting; + if (visible) { + renderAnalysis(rootMethod); + observer.disconnect(); + } + }); + analysisObserver.observe(tableWrapper); + } } } async function renderAnalysis(rootMethod: RootNode) { + if (!tableContainer) { + return; + } const methodMap: Map = new Map(); addNodeToMap(methodMap, rootMethod); diff --git a/log-viewer/modules/calltree-view/CalltreeView.ts b/log-viewer/modules/calltree-view/CalltreeView.ts index bb298bbd..c46deda4 100644 --- a/log-viewer/modules/calltree-view/CalltreeView.ts +++ b/log-viewer/modules/calltree-view/CalltreeView.ts @@ -1,7 +1,7 @@ /* * Copyright (c) 2022 Certinia Inc. All rights reserved. */ -// todo: add breakcrumbs back? - I will do this but in a later PR + better +// todo: add breadcrumbs back? - I will do this but in a later PR + better // //todo: ** future ** //todo: show total and self as percentage of total? + do the same on the analysis view? @@ -24,29 +24,24 @@ import { hostService } from '../services/VSCodeService'; import treeViewStyles from './TreeView.scss'; let calltreeTable: Tabulator; -let tableContainer: HTMLDivElement; +let tableContainer: HTMLDivElement | null; @customElement('call-tree-view') export class CalltreeView extends LitElement { @property() timelineRoot: RootNode | null = null; + get _callTreeTableWrapper(): HTMLDivElement | null { + return (tableContainer = this.renderRoot?.querySelector('#call-tree-table') ?? null); + } + constructor() { super(); } updated(changedProperties: PropertyValues): void { - const timlineRoot = changedProperties.has('timelineRoot'); - if (this.timelineRoot && timlineRoot) { - const calltreeContainer = this.shadowRoot?.getElementById( - 'call-tree-table-container' - ) as HTMLDivElement; - - tableContainer = this.shadowRoot?.getElementById('call-tree-table') as HTMLDivElement; - - if (calltreeContainer) { - initCalltree(calltreeContainer, tableContainer, this.timelineRoot); - } + if (this.timelineRoot && changedProperties.has('timelineRoot')) { + this._appendTableWhenVisible(); } } @@ -128,22 +123,20 @@ export class CalltreeView extends LitElement { collapseAll(calltreeTable.getRows()); calltreeTable.restoreRedraw(); } -} -export function initCalltree( - callTreeView: HTMLDivElement, - callTreeTable: HTMLDivElement, - rootMethod: RootNode -) { - if (callTreeView) { - const analysisObserver = new IntersectionObserver((entries, observer) => { - const visible = entries[0].isIntersecting; - if (visible) { - renderCallTree(callTreeTable, rootMethod); - observer.disconnect(); - } - }); - analysisObserver.observe(callTreeView); + _appendTableWhenVisible() { + const callTreeWrapper = this._callTreeTableWrapper; + const rootMethod = this.timelineRoot; + if (callTreeWrapper && rootMethod) { + const analysisObserver = new IntersectionObserver((entries, observer) => { + const visible = entries[0].isIntersecting; + if (visible) { + renderCallTree(callTreeWrapper, rootMethod); + observer.disconnect(); + } + }); + analysisObserver.observe(callTreeWrapper); + } } } @@ -152,6 +145,8 @@ export async function renderCallTree( rootMethod: RootNode ): Promise { if (calltreeTable) { + // Ensure the table is fully visible before attempting to do things e.g go to rows. + // Otherwise there are visible rendering issues. await new Promise((resolve, reject) => { const visibilityObserver = new IntersectionObserver((entries, observer) => { const visible = entries[0].isIntersecting && entries[0].intersectionRatio > 0; @@ -398,6 +393,10 @@ function toCallTree(nodes: LogLine[]): CalltreeRow[] | undefined { } export async function goToRow(timestamp: number) { + if (!tableContainer) { + return; + } + document.dispatchEvent(new CustomEvent('show-tab', { detail: { tabid: 'tree-tab' } })); await renderCallTree(tableContainer, rootMethod); diff --git a/log-viewer/modules/components/BadgeBase.ts b/log-viewer/modules/components/BadgeBase.ts new file mode 100644 index 00000000..23be3c69 --- /dev/null +++ b/log-viewer/modules/components/BadgeBase.ts @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Certinia Inc. All rights reserved. + */ +import { provideVSCodeDesignSystem, vsCodeTag } from '@vscode/webview-ui-toolkit'; +import { LitElement, css, html } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; + +import { globalStyles } from '../global.styles'; + +provideVSCodeDesignSystem().register(vsCodeTag()); + +@customElement('badge-base') +export class BadgeBase extends LitElement { + @property() + status: 'success' | 'failure' | 'neutral' = 'neutral'; + colorMap = new Map([ + ['success', 'success-tag'], + ['failure', 'failure-tag'], + ]); + + static styles = [ + globalStyles, + css` + :host { + } + + .tag { + font-family: monospace; + font-size: inherit; + } + + .tag::part(control) { + color: var(--vscode-editor-foreground); + background-color: var(--button-icon-hover-background, rgba(90, 93, 94, 0.31)); + text-transform: inherit; + border: none; + } + .success-tag::part(control) { + background-color: rgba(128, 255, 128, 0.2); + } + + .failure-tag::part(control) { + background-color: var(--notification-error-background); + } + `, + ]; + + render() { + const statusTag = this.colorMap.get(this.status); + + return html``; + } +} diff --git a/log-viewer/modules/database-view/DatabaseSection.ts b/log-viewer/modules/database-view/DatabaseSection.ts index 4ac80fcd..634d182e 100644 --- a/log-viewer/modules/database-view/DatabaseSection.ts +++ b/log-viewer/modules/database-view/DatabaseSection.ts @@ -4,6 +4,7 @@ import { LitElement, css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import '../components/BadgeBase'; import { globalStyles } from '../global.styles'; import { Method } from '../parsers/TreeParser'; @@ -40,7 +41,9 @@ export class DatabaseSection extends LitElement { return html`
- ${this.title} (Count: ${totalCount}, Rows: ${totalRows}) + ${this.title} + Count: ${totalCount} + Rows: ${totalRows}
`; } diff --git a/log-viewer/modules/database-view/DatabaseView.ts b/log-viewer/modules/database-view/DatabaseView.ts index 492be109..76713554 100644 --- a/log-viewer/modules/database-view/DatabaseView.ts +++ b/log-viewer/modules/database-view/DatabaseView.ts @@ -29,8 +29,7 @@ import databaseViewStyles from './DatabaseView.scss'; let soqlTable: Tabulator; let dmlTable: Tabulator; -let dmlTableContainer: HTMLDivElement; -let soqlTableContainer: HTMLDivElement; + @customElement('database-view') export class DatabaseView extends LitElement { @property() @@ -42,22 +41,21 @@ export class DatabaseView extends LitElement { @state() soqlLines: SOQLExecuteBeginLine[] = []; + get _dmlTableWrapper(): HTMLDivElement | null { + return this.renderRoot?.querySelector('#db-dml-table') ?? null; + } + + get _soqlTableWrapper(): HTMLDivElement | null { + return this.renderRoot?.querySelector('#db-soql-table') ?? null; + } + constructor() { super(); } - async updated(changedProperties: PropertyValues): Promise { - const timlineRoot = changedProperties.has('timelineRoot'); - if (this.timelineRoot && timlineRoot) { - DatabaseAccess.create(this.timelineRoot); - this.dmlLines = DatabaseAccess.instance()?.getDMLLines() || []; - this.soqlLines = DatabaseAccess.instance()?.getSOQLLines() || []; - - dmlTableContainer = this.shadowRoot?.getElementById('db-dml-table') as HTMLDivElement; - soqlTableContainer = this.shadowRoot?.getElementById('db-soql-table') as HTMLDivElement; - if (dmlTableContainer) { - initDBRender(dmlTableContainer, soqlTableContainer, this.dmlLines, this.soqlLines); - } + updated(changedProperties: PropertyValues): void { + if (this.timelineRoot && changedProperties.has('timelineRoot')) { + this._appendTableWhenVisible(); } } @@ -126,7 +124,7 @@ export class DatabaseView extends LitElement { id="db-soql-groupby-checkbox" type="checkbox" checked - @change=${this.soqlGroupBy} + @change=${this._soqlGroupBy} /> @@ -144,29 +142,32 @@ export class DatabaseView extends LitElement { dmlTable.setGroupBy(checkBox.checked ? 'dml' : ''); } - soqlGroupBy(event: Event) { + _soqlGroupBy(event: Event) { const checkBox = event.target as HTMLInputElement; soqlTable.setGroupBy(checkBox.checked ? 'soql' : ''); } -} -export async function initDBRender( - dmlTable: HTMLElement, - soqlTable: HTMLElement, - dmlLines: DMLBeginLine[], - soqlLines: SOQLExecuteBeginLine[] -) { - if (dmlTable) { - const dbObserver = new IntersectionObserver((entries, observer) => { - const visible = entries[0].isIntersecting; - if (visible) { - observer.disconnect(); - Tabulator.registerModule([RowKeyboardNavigation]); - renderDMLTable(dmlTable, dmlLines); - renderSOQLTable(soqlTable, soqlLines); - } - }); - dbObserver.observe(dmlTable); + async _appendTableWhenVisible() { + const dmlTableWrapper = this._dmlTableWrapper; + const soqlTableWrapper = this._soqlTableWrapper; + const treeRoot = this.timelineRoot; + if (dmlTableWrapper && soqlTableWrapper && treeRoot) { + const dbObserver = new IntersectionObserver(async (entries, observer) => { + const visible = entries[0].isIntersecting; + if (visible) { + observer.disconnect(); + + const dbAccess = await DatabaseAccess.create(treeRoot); + this.dmlLines = dbAccess.getDMLLines() || []; + this.soqlLines = dbAccess.getSOQLLines() || []; + + Tabulator.registerModule([RowKeyboardNavigation]); + renderDMLTable(dmlTableWrapper, this.dmlLines); + renderSOQLTable(soqlTableWrapper, this.soqlLines); + } + }); + dbObserver.observe(dmlTableWrapper); + } } } diff --git a/log-viewer/modules/navbar/NavBar.ts b/log-viewer/modules/navbar/NavBar.ts index 9fc7e529..1af0cfaf 100644 --- a/log-viewer/modules/navbar/NavBar.ts +++ b/log-viewer/modules/navbar/NavBar.ts @@ -5,6 +5,7 @@ import { provideVSCodeDesignSystem, vsCodeButton, vsCodeTag } from '@vscode/webv import { LitElement, css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import '../components/BadgeBase'; import '../components/LogTitle'; import { globalStyles } from '../global.styles'; import { notificationStyles } from '../notification.styles'; @@ -110,11 +111,11 @@ export class NavBar extends LitElement { const sizeText = this.logSize ? (this.logSize / 1000000).toFixed(2) + ' MB' : '', elapsedText = this._toDuration(this.logDuration); - const statusClass = + const status = this.notifications.length > 0 - ? 'failure-tag' + ? 'failure' : this.logStatus !== 'Processing...' - ? 'success-tag' + ? 'success' : ''; return html` @@ -122,9 +123,9 @@ export class NavBar extends LitElement { diff --git a/log-viewer/modules/notifications/NotificationTag.ts b/log-viewer/modules/notifications/NotificationTag.ts index 7c568008..9fca9dda 100644 --- a/log-viewer/modules/notifications/NotificationTag.ts +++ b/log-viewer/modules/notifications/NotificationTag.ts @@ -1,14 +1,15 @@ /* * Copyright (c) 2023 Certinia Inc. All rights reserved. */ -import { provideVSCodeDesignSystem, vsCodeButton, vsCodeTag } from '@vscode/webview-ui-toolkit'; +import { provideVSCodeDesignSystem, vsCodeButton } from '@vscode/webview-ui-toolkit'; import { LitElement, css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; +import '../components/BadgeBase'; import { globalStyles } from '../global.styles'; import './NotificationPanel'; -provideVSCodeDesignSystem().register(vsCodeButton(), vsCodeTag()); +provideVSCodeDesignSystem().register(vsCodeButton()); @customElement('notification-tag') export class NotificationTag extends LitElement { @@ -59,27 +60,6 @@ export class NotificationTag extends LitElement { text-align: center; } - .status-tag { - position: relative; - font-family: monospace; - font-size: inherit; - } - - .status-tag::part(control) { - color: var(--vscode-editor-foreground); - background-color: var(--button-icon-hover-background, rgba(90, 93, 94, 0.31)); - text-transform: inherit; - border: none; - } - - .success-tag::part(control) { - background-color: rgba(128, 255, 128, 0.2); - } - - .failure-tag::part(control) { - background-color: rgba(255, 128, 128, 0.2); - } - .tag-panel { position: absolute; top: calc(100% + 10px); @@ -94,13 +74,13 @@ export class NotificationTag extends LitElement { ]; render() { - const issueColor = this.notifications.length > 0 ? 'failure-tag' : 'success-tag'; + const status = this.notifications.length > 0 ? 'failure' : 'success'; return html`