diff options
Diffstat (limited to 'pw_web/log-viewer')
8 files changed, 103 insertions, 85 deletions
diff --git a/pw_web/log-viewer/src/components/log-list/log-list.styles.ts b/pw_web/log-viewer/src/components/log-list/log-list.styles.ts index 6dabfb4a0..a10b36360 100644 --- a/pw_web/log-viewer/src/components/log-list/log-list.styles.ts +++ b/pw_web/log-viewer/src/components/log-list/log-list.styles.ts @@ -88,9 +88,9 @@ export const styles = css` --icon-color: var(--sys-log-viewer-color-debug); } - .log-row--warning .cell-content--icon, - .log-row--error .cell-content--icon, - .log-row--critical .cell-content--icon { + .log-row--warning .cell-icon, + .log-row--error .cell-icon, + .log-row--critical .cell-icon { color: var(--icon-color); } @@ -138,7 +138,7 @@ export const styles = css` display: block; grid-row: 1; overflow: hidden; - padding: 0.5rem 1rem; + padding: var(--sys-log-viewer-table-cell-padding); text-align: left; text-overflow: ellipsis; } @@ -212,10 +212,6 @@ export const styles = css` width: 1rem; } - .cell-content--icon { - padding-top: 0.125rem; - } - .cell-icon { display: block; font-variation-settings: @@ -223,8 +219,11 @@ export const styles = css` 'wght' 400, 'GRAD' 200, 'opsz' 58; - font-size: 1.25rem; + font-size: var(--sys-log-viewer-table-cell-icon-size); user-select: none; + display: grid; + place-content: center; + place-items: center; } .overflow-indicator { @@ -268,8 +267,8 @@ export const styles = css` mark { background-color: var(--sys-log-viewer-color-table-mark); - border-radius: 2px; - color: var(--sys-log-viewer-color-table-mark); + border-radius: 4px; + color: var(--sys-log-viewer-color-table-mark-text); outline: 1px solid var(--sys-log-viewer-color-table-mark); } diff --git a/pw_web/log-viewer/src/components/log-list/log-list.ts b/pw_web/log-viewer/src/components/log-list/log-list.ts index ca450fc94..58d635ce7 100644 --- a/pw_web/log-viewer/src/components/log-list/log-list.ts +++ b/pw_web/log-viewer/src/components/log-list/log-list.ts @@ -59,14 +59,6 @@ export class LogList extends LitElement { @state() private _isOverflowingToRight = false; - /** A number representing the scroll percentage in the horizontal direction. */ - @state() - private _scrollPercentageLeft = 0; - - /** Indicates whether to enable autosizing of incoming log entries. */ - @state() - private _autosizeLocked = false; - /** * Indicates whether to automatically scroll the table container to the bottom * when new log entries are added. @@ -74,31 +66,42 @@ export class LogList extends LitElement { @state() private _autoscrollIsEnabled = true; + /** A number representing the scroll percentage in the horizontal direction. */ + @state() + private _scrollPercentageLeft = 0; + @query('.table-container') private _tableContainer!: HTMLDivElement; @query('table') private _table!: HTMLTableElement; @query('tbody') private _tableBody!: HTMLTableSectionElement; @queryAll('tr') private _tableRows!: HTMLTableRowElement[]; - /** - * Data used for column resizing including the column index, the starting - * mouse position (X-coordinate), and the initial width of the column. - */ - private columnResizeData: { - columnIndex: number; - startX: number; - startWidth: number; - } | null = null; + /** Indicates whether to enable autosizing of incoming log entries. */ + private _autosizeLocked = false; /** The number of times the `logs` array has been updated. */ private logUpdateCount: number = 0; + + /** The last known vertical scroll position of the table container. */ + private lastScrollTop: number = 0; + /** The maximum number of log entries to render in the list. */ private readonly MAX_ENTRIES = 100_000; + /** The maximum number of log updates until autosize is disabled. */ private readonly AUTOSIZE_LIMIT: number = 8; + /** The minimum width (in px) for table columns. */ private readonly MIN_COL_WIDTH: number = 52; - /** The last known vertical scroll position of the table container. */ - private lastScrollTop: number = 0; + + /** + * Data used for column resizing including the column index, the starting + * mouse position (X-coordinate), and the initial width of the column. + */ + private columnResizeData: { + columnIndex: number; + startX: number; + startWidth: number; + } | null = null; firstUpdated() { setInterval(() => this.updateHorizontalOverflowState(), 1000); @@ -148,9 +151,6 @@ export class LogList extends LitElement { if (this.logUpdateCount >= this.AUTOSIZE_LIMIT) { this._autosizeLocked = true; } - if (!this._autosizeLocked) { - this.autosizeColumns(); - } }; /** Called when the Lit virtualizer updates its range of entries. */ @@ -170,6 +170,11 @@ export class LogList extends LitElement { }, 0); // Complete any rendering tasks before scrolling } + private onJumpToBottomButtonClick() { + this._autoscrollIsEnabled = true; + this.scrollTableToBottom(); + } + /** * Calculates the maximum column widths for the table and updates the table * rows. @@ -223,7 +228,7 @@ export class LogList extends LitElement { columnValue = `${col.manualWidth}px`; } else { if (i === 0) { - columnValue = '3.25rem'; + columnValue = '3rem'; } else { const chWidth = col.characterLength; const padding = 34; @@ -249,11 +254,11 @@ export class LogList extends LitElement { * @param {string} text - The table cell text to be processed. */ private highlightMatchedText(text: string): TemplateResult[] { - const searchPhrase = this.searchText?.replace(/(^"|')|("|'$)/g, ''); - if (!searchPhrase) { + if (!this.searchText) { return [html`${text}`]; } + const searchPhrase = this.searchText?.replace(/(^"|')|("|'$)/g, ''); const escapedsearchText = searchPhrase.replace( /[.*+?^${}()|[\]\\]/g, '\\$&', @@ -285,34 +290,36 @@ export class LogList extends LitElement { const scrollY = container.scrollHeight - currentScrollTop - container.clientHeight; const maxScrollLeft = container.scrollWidth - containerWidth; - const rowHeight = this._tableRows[0].offsetHeight; // Determine scroll direction and update the last known scroll position const isScrollingVertically = currentScrollTop !== this.lastScrollTop; const isScrollingUp = currentScrollTop < this.lastScrollTop; this.lastScrollTop = currentScrollTop; - // Only run autoscroll logic if the user is scrolling vertically + const logsAreCleared = this.logs.length == 0; + + if (logsAreCleared) { + this._autoscrollIsEnabled = true; + return; + } + + // Run autoscroll logic if scrolling vertically if (!isScrollingVertically) { this._scrollPercentageLeft = scrollLeft / maxScrollLeft || 0; return; } - // User is scrolling up, disable autoscroll + // Scroll direction up, disable autoscroll if (isScrollingUp) { this._autoscrollIsEnabled = false; return; } - // User is scrolling down, enable autoscroll if they're near the bottom + // Scroll direction down, enable autoscroll if near the bottom if (Math.abs(scrollY) <= 1) { this._autoscrollIsEnabled = true; return; } - - if (Math.round(scrollY - rowHeight) >= 1) { - this._autoscrollIsEnabled = false; - } }; /** @@ -523,7 +530,7 @@ export class LogList extends LitElement { return html` <td ?hidden=${!isVisible}> - <div class="cell-content cell-content--icon"> + <div class="cell-content"> <md-icon class="cell-icon" title="${toTitleCase(field.value.toString())}" @@ -570,7 +577,7 @@ export class LogList extends LitElement { <md-filled-button class="jump-to-bottom-btn" title="Jump to Bottom" - @click="${this.scrollTableToBottom}" + @click="${this.onJumpToBottomButtonClick}" leading-icon data-visible="${this._autoscrollIsEnabled ? 'false' : 'true'}" > diff --git a/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.styles.ts b/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.styles.ts index 0f17bc9f1..0277857eb 100644 --- a/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.styles.ts +++ b/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.styles.ts @@ -83,10 +83,10 @@ export const styles = css` font-size: 1rem; height: 0.75rem; line-height: 0.75; - max-width: 100%; + max-width: 30rem; overflow: hidden; padding: 0.5rem 1rem; - width: 25rem; + width: 100%; } input[type='text'] { diff --git a/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.ts b/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.ts index f31f8e129..8c0226f7b 100644 --- a/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.ts +++ b/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.ts @@ -50,9 +50,6 @@ export class LogViewControls extends LitElement { _stateStore: StateStore = new LocalStorageState(); @state() - _state: State; - - @state() _viewTitle = 'Log View'; @state() @@ -66,6 +63,8 @@ export class LogViewControls extends LitElement { @queryAll('.item-checkboxes') _itemCheckboxes!: HTMLCollection[]; + private _state: State; + /** The timer identifier for debouncing search input. */ private _inputDebounceTimer: number | null = null; diff --git a/pw_web/log-viewer/src/components/log-view/log-view.ts b/pw_web/log-viewer/src/components/log-view/log-view.ts index e975c044f..900637e5c 100644 --- a/pw_web/log-viewer/src/components/log-view/log-view.ts +++ b/pw_web/log-viewer/src/components/log-view/log-view.ts @@ -55,47 +55,44 @@ export class LogView extends LitElement { @state() _lineWrap = false; + /** The field keys (column values) for the incoming log entries. */ + @state() + private _columnData: TableColumn[] = []; + + /** A string representing the value contained in the search field. */ + @state() + public searchText = ''; + + /** A StateStore object that stores state of views */ + @state() + _stateStore: StateStore = new LocalStorageState(); + + @query('log-list') _logList!: LogList; + /** * An array containing the logs that remain after the current filter has been * applied. */ - @state() private _filteredLogs: LogEntry[] = []; - /** The field keys (column values) for the incoming log entries. */ - @state() - private _columnData: TableColumn[] = []; - /** A function used for filtering rows that contain a certain substring. */ - @state() private _stringFilter: FilterFunction = () => true; /** * A function used for filtering rows that contain a timestamp within a * certain window. */ - @state() private _timeFilter: FilterFunction = () => true; - /** A string representing the value contained in the search field. */ - @state() - public searchText = ''; - - /** A StateStore object that stores state of views */ - @state() - _stateStore: StateStore = new LocalStorageState(); - - @state() - _state: State; - - @query('log-list') _logList!: LogList; + private _state: State; private _debounceTimeout: NodeJS.Timeout | null = null; + /** The number of elements in the `logs` array since last updated. */ + private _lastKnownLogLength: number = 0; + /** The amount of time, in ms, before the filter expression is executed. */ private readonly FILTER_DELAY = 100; - /** The number of elements in the `logs` array since last updated. */ - private lastKnownLogLength: number = 0; constructor() { super(); @@ -117,10 +114,13 @@ export class LogView extends LitElement { super.updated(changedProperties); if (changedProperties.has('logs')) { - const newLogs = this.logs.slice(this.lastKnownLogLength); - this.lastKnownLogLength = this.logs.length; + const newLogs = this.logs.slice(this._lastKnownLogLength); + this._lastKnownLogLength = this.logs.length; this.updateFieldsFromNewLogs(newLogs); + } + + if (changedProperties.has('logs') || changedProperties.has('searchText')) { this.filterLogs(); } @@ -263,16 +263,20 @@ export class LogView extends LitElement { } /** - * Combines constituent filter expressions and filters the logs. The filtered - * logs are stored in the `_filteredLogs` state property. + * Combines filter expressions and filters the logs. The filtered + * logs are stored in the `_filteredLogs` property. */ private filterLogs() { const combinedFilter = (logEntry: LogEntry) => this._timeFilter(logEntry) && this._stringFilter(logEntry); - this._filteredLogs = JSON.parse( - JSON.stringify(this.logs.filter(combinedFilter)), - ); + const newFilteredLogs = this.logs.filter(combinedFilter); + + if ( + JSON.stringify(newFilteredLogs) !== JSON.stringify(this._filteredLogs) + ) { + this._filteredLogs = newFilteredLogs; + } } private updateColumnData(event: CustomEvent) { @@ -322,7 +326,7 @@ export class LogView extends LitElement { render() { return html` <log-view-controls - .columnData=${[...this._columnData]} + .columnData=${this._columnData} .viewId=${this.id} .hideCloseButton=${!this.isOneOfMany} .stateStore=${this._stateStore} diff --git a/pw_web/log-viewer/src/components/log-viewer.styles.ts b/pw_web/log-viewer/src/components/log-viewer.styles.ts index bc155b383..0bf3a88e6 100644 --- a/pw_web/log-viewer/src/components/log-viewer.styles.ts +++ b/pw_web/log-viewer/src/components/log-viewer.styles.ts @@ -43,6 +43,10 @@ export const styles = css` --sys-log-viewer-height: 100%; --sys-log-viewer-view-outline-width: 1px; --sys-log-viewer-view-corner-radius: 0.5rem; + + /* Log List */ + --sys-log-viewer-table-cell-padding: 0.375rem 0.75rem; + --sys-log-viewer-table-cell-icon-size: 1.125rem; } .grid-container { diff --git a/pw_web/log-viewer/src/components/log-viewer.ts b/pw_web/log-viewer/src/components/log-viewer.ts index 91dba1e76..06c695309 100644 --- a/pw_web/log-viewer/src/components/log-viewer.ts +++ b/pw_web/log-viewer/src/components/log-viewer.ts @@ -51,12 +51,11 @@ export class LogViewer extends LitElement { @state() _logViews: LogView[] = []; - /** A StateStore object that stores state of views */ + /** An object that stores the state of log views */ @state() _stateStore: StateStore; - @state() - _state: State; + private _state: State; constructor(state: StateStore) { super(); @@ -175,7 +174,7 @@ export class LogViewer extends LitElement { (view) => html` <log-view id=${view.id} - .logs=${[...this.logs]} + .logs=${this.logs} .isOneOfMany=${this._logViews.length > 1} .stateStore=${this._stateStore} @add-view="${this.addLogView}" diff --git a/pw_web/log-viewer/src/index.css b/pw_web/log-viewer/src/index.css index f16bd1612..04a79a2c2 100644 --- a/pw_web/log-viewer/src/index.css +++ b/pw_web/log-viewer/src/index.css @@ -44,10 +44,16 @@ button { main { height: 100vh; - padding: 2rem; + padding: 16px; width: 100vw; } +@media (min-width: 840px) { + main { + padding: 24px; + } +} + a { color: var(--md-sys-color-primary); font-weight: 500; |