| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as constant from '../src/constants'; |
| import { LogHeader } from './log_header'; |
| import { LogRow } from './log_row'; |
| import { FfxLogData } from '../src/log_data'; |
| import { LogField } from '../src/fields'; |
| import { FilterExpression } from '../src/filter'; |
| import { State } from '../src/state'; |
| |
| export const WRAP_LOG_TEXT_ATTR = 'wrap-log-text'; |
| |
| export class LogList { |
| private logList: HTMLTableElement; |
| private logHeader: LogHeader; |
| private hasPreviousLog: boolean = false; |
| private maxWidths: Array<number>; |
| |
| constructor(private state: State) { |
| this.maxWidths = new Array(Object.keys(state.currentFields).length).fill(0); |
| this.logHeader = new LogHeader(state); |
| this.logList = this.initLogList(); |
| this.logWrapping = state.shouldWrapLogs; |
| |
| state.addEventListener('filterChange', (e) => { |
| const event = e as CustomEvent; |
| const filter: FilterExpression = event.detail.filter; |
| const logRows = Array.from(this.logList.children).slice(1); |
| |
| if (filter.isEmpty()) { |
| for (const element of logRows) { |
| element.removeAttribute('hidden'); |
| } |
| } else { |
| for (const element of logRows) { |
| if (filter.accepts(element)) { |
| element.removeAttribute('hidden'); |
| } else { |
| element.setAttribute('hidden', ''); |
| } |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Returns the core element. |
| */ |
| get element() { |
| return this.logList; |
| } |
| |
| /** |
| * Appends a log to the current list of logs |
| */ |
| public addLog(log: FfxLogData) { |
| this.addLogElement(log); |
| this.hasPreviousLog = true; |
| } |
| |
| /** |
| * Resets the state and contents of the webview to contain nothing. |
| * The persistent state is maintained as that represents user selections. |
| */ |
| public reset() { |
| this.logList.innerHTML = ''; |
| this.logList.appendChild(this.logHeader.element); |
| this.hasPreviousLog = false; |
| } |
| |
| /** |
| * Defines whether or not to wrap the log text. |
| * |
| * @param logWrapActive if true the log text will be wrapped |
| */ |
| public set logWrapping(logWrapActive: boolean) { |
| if (logWrapActive) { |
| this.logList.setAttribute(WRAP_LOG_TEXT_ATTR, 'true'); |
| } else { |
| this.logList.removeAttribute(WRAP_LOG_TEXT_ATTR); |
| } |
| } |
| |
| /** |
| * Initializes the element that contains the list of logs. |
| * |
| * @return the log list element. |
| */ |
| private initLogList(): HTMLTableElement { |
| let logList = document.createElement('table'); |
| logList.appendChild(this.logHeader.element); |
| logList.id = 'log-list'; |
| return logList; |
| } |
| |
| private addLogElement(log: FfxLogData) { |
| const logsToDrop = this.logList.children.length - constant.MAX_LOGS; |
| for (let i = 0; i < logsToDrop; i++) { |
| this.popLogElement(); |
| } |
| this.appendLogElement(log); |
| if (!this.logList.parentElement?.matches(':hover')) { |
| this.logList.parentElement?.scrollTo(-1, this.logList.scrollHeight); |
| }; |
| } |
| |
| private appendLogElement(log: FfxLogData) { |
| const newRow = new LogRow(log, this.hasPreviousLog); |
| const element = newRow.element; |
| if (!this.filtersAllow(element)) { |
| element.setAttribute('hidden', ''); |
| } |
| this.logList.appendChild(element); |
| this.setMaxCellWidth(element); |
| } |
| |
| private setMaxCellWidth(row: Element) { |
| for (const cell in Array.from(row.children)) { |
| let id = row.children[cell].id as LogField; |
| let width = this.maxWidths[cell]; |
| if (width < row.children[cell].scrollWidth) { |
| this.maxWidths[cell] = row.children[cell].scrollWidth; |
| this.logHeader.setMaxCellWidth(id, this.maxWidths[cell]); |
| } |
| } |
| } |
| |
| private popLogElement() { |
| this.logList.removeChild(this.logList.children[-1]); |
| } |
| |
| private filtersAllow(el: Element) { |
| return this.state.currentFilter.accepts(el); |
| } |
| } |