• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { LitTableColumn } from './lit-table-column';
17import { LitProgressBar } from './../progress-bar/LitProgressBar';
18import { element } from '../BaseElement';
19import '../utils/Template';
20import { TableRowObject } from './TableRowObject';
21import { ExcelFormater } from '../utils/ExcelFormater';
22import { LitIcon } from '../icon/LitIcon';
23import { NodeType } from '../../js-heap/model/DatabaseStruct';
24import { ConstructorType } from '../../js-heap/model/UiStruct';
25import { JsCpuProfilerStatisticsStruct } from '../../trace/bean/JsStruct';
26import {
27  iconPadding,
28  iconWidth,
29  createDownUpSvg,
30  litTableHtml,
31  exportData,
32  formatExportData,
33  recursionExportTableData,
34  addCopyEventListener,
35  addSelectAllBox,
36  fixed,
37  formatName
38} from './LitTableHtml';
39
40@element('lit-table')
41export class LitTable extends HTMLElement {
42  meauseRowElement: HTMLDivElement | undefined;
43  currentRecycleList: HTMLDivElement[] = [];
44  currentTreeDivList: HTMLDivElement[] = [];
45  public rememberScrollTop = false;
46  public getItemTextColor?: (data: any) => string;
47  public itemTextHandleMap: Map<string, (value: any) => string> = new Map<string, (value: any) => string>();
48  public exportTextHandleMap: Map<string, (value: any) => string> = new Map<string, (value: any) => string>();
49  public ds: Array<any> = [];
50  public recycleDs: Array<any> = [];
51  public gridTemplateColumns: Array<string> = [];
52  public tableColumns: NodeListOf<LitTableColumn> | undefined;
53  public treeElement: HTMLDivElement | undefined | null;
54  public columns: Array<Element> | null | undefined;
55  public exportLoading: boolean = false;
56  public exportProgress: LitProgressBar | null | undefined;
57  public tableElement: HTMLDivElement | null | undefined;
58  private normalDs: Array<any> = [];
59  /*Grid css layout descriptions are obtained according to the clustern[] nested structure*/
60  private st: HTMLSlotElement | null | undefined;
61  private theadElement: HTMLDivElement | null | undefined;
62  private tbodyElement: HTMLDivElement | undefined | null;
63  private colCount: number = 0;
64  private isRecycleList: boolean = true;
65  private isScrollXOutSide: boolean = false;
66  private value: Array<any> = [];
67  private _mode = TableMode.Expand;
68  private columnResizeEnable: boolean = true;
69  private _isSearch: boolean = false;
70
71  constructor() {
72    super();
73    const shadowRoot = this.attachShadow({ mode: 'open' });
74    shadowRoot.innerHTML = litTableHtml;
75  }
76
77  static get observedAttributes(): string[] {
78    return [
79      'scroll-y',
80      'selectable',
81      'no-head',
82      'grid-line',
83      'defaultOrderColumn',
84      'hideDownload',
85      'noRecycle',
86      'loading',
87      'expand',
88    ];
89  }
90
91  set mode(mode: TableMode) {
92    this._mode = mode;
93  }
94
95  set loading(value: boolean) {
96    this.exportProgress!.loading = value;
97  }
98
99  get hideDownload(): boolean {
100    return this.hasAttribute('hideDownload');
101  }
102
103  set hideDownload(value) {
104    if (value) {
105      this.setAttribute('hideDownload', '');
106    } else {
107      this.removeAttribute('hideDownload');
108    }
109  }
110
111  get selectable(): boolean {
112    return this.hasAttribute('selectable');
113  }
114
115  set selectable(value) {
116    if (value) {
117      this.setAttribute('selectable', '');
118    } else {
119      this.removeAttribute('selectable');
120    }
121  }
122
123  get scrollY(): string {
124    return this.getAttribute('scroll-y') || 'auto';
125  }
126
127  set scrollY(value) {
128    this.setAttribute('scroll-y', value);
129  }
130
131  get dataSource(): any[] {
132    return this.ds || [];
133  }
134
135  set dataSource(value) {
136    if (this.hasAttribute('noRecycle')) {
137      this.ds = value;
138      this.isRecycleList = false;
139      this.renderTable();
140    } else {
141      this.columnResizeEnable = false;
142      this.recycleDataSource = value;
143    }
144  }
145
146  set noRecycle(value) {
147    if (value) {
148      this.setAttribute('noRecycle', '');
149    } else {
150      this.removeAttribute('noRecycle');
151    }
152  }
153
154  get noRecycle(): boolean {
155    return this.hasAttribute('noRecycle');
156  }
157
158  get recycleDataSource(): any[] {
159    return this.ds || [];
160  }
161
162  set isSearch(value: boolean) {
163    this._isSearch = value;
164  }
165
166  set recycleDataSource(value) {
167    if (this.tableElement) {
168      this.isScrollXOutSide = this.tableElement!.scrollWidth > this.tableElement!.clientWidth;
169      this.isRecycleList = true;
170      this.ds = value;
171      if (this.rememberScrollTop) {
172        this.tableElement!.scrollTop = 0;
173        this.tableElement!.scrollLeft = 0;
174      } else {
175        this.tableElement!.scrollTop = 0;
176        this.tableElement!.scrollLeft = 0;
177      }
178      if (this.hasAttribute('tree')) {
179        if (value.length === 0) {
180          this.value = [];
181          this.recycleDs = this.meauseTreeRowElement(value);
182        } else {
183          if (
184            value !== this.value &&
185            this.value.length !== 0 &&
186            !this._isSearch &&
187            this.querySelector('lit-table-column')?.hasAttribute('retract')
188          ) {
189            this.shadowRoot!.querySelector<LitIcon>('.top')!.name = 'up';
190            this.shadowRoot!.querySelector<LitIcon>('.bottom')!.name = 'down';
191          }
192          this._isSearch = false;
193          this.value = value;
194          this.recycleDs = this.meauseTreeRowElement(value, RedrawTreeForm.Retract);
195        }
196      } else {
197        this.recycleDs = this.meauseAllRowHeight(value);
198      }
199    }
200  }
201
202  get snapshotDataSource(): any[] {
203    return this.ds || [];
204  }
205
206  set snapshotDataSource(value) {
207    this.ds = value;
208    if (this.hasAttribute('tree')) {
209      this.recycleDs = this.meauseTreeRowElement(value, RedrawTreeForm.Default);
210    } else {
211      this.recycleDs = this.meauseAllRowHeight(value);
212    }
213  }
214
215  move1px(): void {
216    this.tableElement!.scrollTop = this.tableElement!.scrollTop + 1;
217  }
218
219  dataExportInit(): void {
220    let exportDiv = this.shadowRoot!.querySelector<HTMLDivElement>('.export');
221    exportDiv &&
222      (exportDiv.onclick = (): void => {
223        this.exportData();
224      });
225  }
226
227  exportData(): void {
228    exportData(this);
229  }
230
231  exportExcelData(): void {
232    let now = Date.now();
233    ExcelFormater.testExport(
234      [
235        {
236          columns: this.columns as any[],
237          tables: this.ds,
238          sheetName: `${now}`,
239        },
240      ],
241      `${now}`
242    );
243  }
244
245  formatExportData(dataSource: any[]): any[] {
246    return formatExportData(dataSource, this);
247  }
248
249  formatExportCsvData(dataSource: any[]): string {
250    if (dataSource === undefined || dataSource.length === 0) {
251      return '';
252    }
253    if (this.columns == undefined) {
254      return '';
255    }
256    let str = '';
257    str += this.columns!.map((column) => {
258      let dataIndex = column.getAttribute('data-index');
259      let columnName = column.getAttribute('title');
260      if (columnName === '') {
261        columnName = dataIndex;
262      }
263      return columnName;
264    }).join(',');
265    str += recursionExportTableData(this.columns, dataSource);
266    return str;
267  }
268
269  injectColumns(): void {
270    this.columns = this.st!.assignedElements();
271    this.columns.forEach((column) => {
272      if (column.tagName === 'LIT-TABLE-COLUMN') {
273        this.gridTemplateColumns.push(column.getAttribute('width') || '1fr');
274      }
275    });
276  }
277
278  setStatus(list: any, status: boolean, depth: number = 0): void {
279    this.tableElement!.scrollTop = 0;
280    // 添加depth参数,让切换图标的代码在递归中只走一遍
281    if (depth === 0) {
282      if (status) {
283        this.shadowRoot!.querySelector<LitIcon>('.top')!.name = 'down';
284        this.shadowRoot!.querySelector<LitIcon>('.bottom')!.name = 'up';
285      } else {
286        this.shadowRoot!.querySelector<LitIcon>('.top')!.name = 'up';
287        this.shadowRoot!.querySelector<LitIcon>('.bottom')!.name = 'down';
288      }
289    }
290    for (let item of list) {
291      item.status = status;
292      if (item.children !== undefined && item.children.length > 0) {
293        this.setStatus(item.children, status, depth + 1);
294      }
295    }
296  }
297
298  //当 custom element首次被插入文档DOM时,被调用。
299  connectedCallback(): void {
300    this.st = this.shadowRoot?.querySelector('#slot');
301    this.tableElement = this.shadowRoot?.querySelector('.table');
302    this.exportProgress = this.shadowRoot?.querySelector('#export_progress_bar');
303    this.theadElement = this.shadowRoot?.querySelector('.thead');
304    this.treeElement = this.shadowRoot?.querySelector('.tree');
305    this.tbodyElement = this.shadowRoot?.querySelector('.body');
306    this.tableColumns = this.querySelectorAll<LitTableColumn>('lit-table-column');
307    this.colCount = this.tableColumns!.length;
308    this.dataExportInit();
309    addCopyEventListener(this);
310    this.st?.addEventListener('slotchange', () => {
311      this.theadElement!.innerHTML = '';
312      setTimeout(() => {
313        this.columns = this.st!.assignedElements();
314        let rowElement = document.createElement('div');
315        rowElement.classList.add('th');
316        addSelectAllBox(rowElement, this);
317        let area: Array<any> = [];
318        this.gridTemplateColumns = [];
319        this.resolvingArea(this.columns, 0, 0, area, rowElement);
320        area.forEach((rows, j, array) => {
321          for (let i = 0; i < this.colCount; i++) {
322            if (!rows[i]) rows[i] = array[j - 1][i];
323          }
324        });
325        if (this.selectable) {
326          let s = area.map((a) => '"_checkbox_ ' + a.map((aa: any) => aa.t).join(' ') + '"').join(' ');
327          rowElement.style.gridTemplateColumns = '60px ' + this.gridTemplateColumns.join(' ');
328          rowElement.style.gridTemplateRows = `repeat(${area.length},1fr)`;
329          rowElement.style.gridTemplateAreas = s;
330        } else {
331          let s = area.map((a) => '"' + a.map((aa: any) => aa.t).join(' ') + '"').join(' ');
332          rowElement.style.gridTemplateColumns = this.gridTemplateColumns.join(' ');
333          rowElement.style.gridTemplateRows = `repeat(${area.length},1fr)`;
334          rowElement.style.gridTemplateAreas = s;
335        }
336        this.theadElement!.innerHTML = '';
337        this.theadElement!.append(rowElement);
338        this.treeElement!.style.top = this.theadElement?.clientHeight + 'px';
339      });
340    });
341    this.shadowRoot!.addEventListener('load', function (event) {});
342    this.tableElement!.addEventListener('mouseout', (ev) => this.mouseOut());
343  }
344
345  resolvingArea(columns: any, x: any, y: any, area: Array<any>, rowElement: HTMLDivElement) {
346    columns.forEach((a: any, i: any) => {
347      if (!area[y]) area[y] = [];
348      let key = a.getAttribute('key') || a.getAttribute('title');
349      if (a.tagName === 'LIT-TABLE-GROUP') {
350        let len = a.querySelectorAll('lit-table-column').length;
351        let children = [...a.children].filter((a) => a.tagName !== 'TEMPLATE');
352        if (children.length > 0) {
353          this.resolvingArea(children, x, y + 1, area, rowElement);
354        }
355        for (let j = 0; j < len; j++) {
356          area[y][x] = { x, y, t: key };
357          x++;
358        }
359        let h = document.createElement('div');
360        h.classList.add('td');
361        h.style.justifyContent = a.getAttribute('align');
362        h.style.borderBottom = '1px solid #f0f0f0';
363        h.style.gridArea = key;
364        h.innerText = a.title;
365        if (a.hasAttribute('fixed')) {
366          fixed(h, a.getAttribute('fixed'), '#42b983');
367        }
368        rowElement.append(h);
369      } else if (a.tagName === 'LIT-TABLE-COLUMN') {
370        area[y][x] = { x, y, t: key };
371        x++;
372        let head = this.resolvingAreaColumn(rowElement, a, i, key);
373        this.gridTemplateColumns.push(a.getAttribute('width') || '1fr');
374        let labelArr = a.title.split('/');
375        for (let i = 0; i < labelArr.length; i++) {
376          let titleLabel = document.createElement('label');
377          titleLabel.style.cursor = 'pointer';
378          i == 0 ? (titleLabel.textContent = labelArr[i]) : (titleLabel.textContent = '/' + labelArr[i]);
379          head.appendChild(titleLabel);
380        }
381        if (a.hasAttribute('fixed')) {
382          fixed(head, a.getAttribute('fixed'), '#42b983');
383        }
384        rowElement.append(head);
385      }
386    });
387  };
388
389  resolvingAreaColumn(rowElement: HTMLDivElement, column: any, index: number, key: string): HTMLDivElement {
390    let head: any = document.createElement('div');
391    head.classList.add('td');
392    if ((this.hasAttribute('tree') && index > 1) || (!this.hasAttribute('tree') && index > 0)) {
393      let resizeDiv: HTMLDivElement = document.createElement('div');
394      resizeDiv.classList.add('resize');
395      head.appendChild(resizeDiv);
396      this.resizeEventHandler(rowElement, resizeDiv, index);
397    }
398    this.resolvingAreaColumnRetract(column, head);
399    this.resolvingAreaColumnOrder(column, index, key, head);
400    this.resolvingAreaColumnButton(column, key, head);
401    head.style.justifyContent = column.getAttribute('align');
402    head.style.gridArea = key;
403    return head;
404  }
405
406  resolvingAreaColumnRetract(column: any, columnHead: HTMLDivElement): void {
407    if (column.hasAttribute('retract')) {
408      let expand = document.createElement('div');
409      expand.classList.add('expand');
410      expand.style.display = 'grid';
411      columnHead.append(expand);
412      let top = document.createElement('lit-icon') as LitIcon;
413      top.classList.add('top');
414      top.name = 'up';
415      expand.append(top);
416      let bottom = document.createElement('lit-icon') as LitIcon;
417      bottom.classList.add('bottom');
418      bottom.name = 'down';
419      expand.append(bottom);
420      expand.addEventListener('click', (e) => {
421        if (top.name == 'up' && bottom.name == 'down') {
422          top.name = 'down';
423          bottom.name = 'up';
424          // 一键展开
425          this.setStatus(this.value, true);
426          this.recycleDs = this.meauseTreeRowElement(this.value, RedrawTreeForm.Expand);
427        } else {
428          top.name = 'up';
429          bottom.name = 'down';
430          // 一键收起
431          this.setStatus(this.value, false);
432          this.recycleDs = this.meauseTreeRowElement(this.value, RedrawTreeForm.Retract);
433        }
434        e.stopPropagation();
435      });
436    }
437  }
438
439  resolvingAreaColumnButton(column: any, key: string, head: HTMLDivElement) {
440    if (column.hasAttribute('button')) {
441      let buttonIcon = document.createElement('button');
442      buttonIcon.innerHTML = 'GetBusyTime(ms)';
443      buttonIcon.classList.add('button-icon');
444      head.appendChild(buttonIcon);
445      buttonIcon.addEventListener('click', (event) => {
446        this.dispatchEvent(
447          new CustomEvent('button-click', {
448            detail: {
449              key: key,
450            },
451            composed: true,
452          })
453        );
454        event.stopPropagation();
455      });
456    }
457  }
458
459  resolvingAreaColumnOrder(column: any, index: number, key: string,columnHead: any): void {
460    if (column.hasAttribute('order')) {
461      (columnHead as any).sortType = 0;
462      columnHead.classList.add('td-order');
463      columnHead.style.position = 'relative';
464      let { upSvg, downSvg } = createDownUpSvg(index, columnHead);
465      columnHead.onclick = () => {
466        if (this.isResize || this.resizeColumnIndex !== -1) {
467          return;
468        }
469        this?.shadowRoot?.querySelectorAll('.td-order svg').forEach((it: any) => {
470          it.setAttribute('fill', 'let(--dark-color1,#212121)');
471          it.sortType = 0;
472          it.style.display = 'none';
473        });
474        if (columnHead.sortType == undefined || columnHead.sortType == null) {
475          columnHead.sortType = 0;
476        } else if (columnHead.sortType === 2) {
477          columnHead.sortType = 0;
478        } else {
479          columnHead.sortType += 1;
480        }
481        upSvg.setAttribute('fill', 'let(--dark-color1,#212121)');
482        downSvg.setAttribute('fill', 'let(--dark-color1,#212121)');
483        upSvg.style.display = columnHead.sortType === 1 ? 'block' : 'none';
484        downSvg.style.display = columnHead.sortType === 2 ? 'block' : 'none';
485        switch (columnHead.sortType) {
486          case 1:
487            this.theadElement!.setAttribute('sort', '');
488            break;
489          case 2:
490            break;
491          default:
492            this.theadElement!.removeAttribute('sort');
493            break;
494        }
495        this.dispatchEvent(
496          new CustomEvent('column-click', {
497            detail: {
498              sort: columnHead.sortType,
499              key: key,
500            },
501            composed: true,
502          })
503        );
504      };
505    }
506  }
507
508  private isResize: boolean = false;
509  private resizeColumnIndex: number = -1;
510  private resizeDownX: number = 0;
511  private columnMinWidth: number = 50;
512  private beforeResizeWidth: number = 0;
513
514  resizeEventHandler(header: HTMLDivElement, element: HTMLDivElement, index: number): void {
515   this.resizeMouseMoveEventHandler(header);
516    header.addEventListener('mouseup', (event) => {
517      if (!this.columnResizeEnable) return;
518      this.isResize = false;
519      this.resizeDownX = 0;
520      header.style.cursor = 'pointer';
521      setTimeout(() => {
522        this.resizeColumnIndex = -1;
523      }, 100);
524      event.stopPropagation();
525      event.preventDefault();
526    });
527    header.addEventListener('mouseleave', (event) => {
528      if (!this.columnResizeEnable) return;
529      event.stopPropagation();
530      event.preventDefault();
531      this.isResize = false;
532      this.resizeDownX = 0;
533      this.resizeColumnIndex = -1;
534      header.style.cursor = 'pointer';
535    });
536    element.addEventListener('mousedown', (event) => {
537      if (event.button === 0) {
538        if (!this.columnResizeEnable) return;
539        this.isResize = true;
540        this.resizeColumnIndex = index;
541        this.resizeDownX = event.clientX;
542        let pre = header.childNodes.item(this.resizeColumnIndex - 1) as HTMLDivElement;
543        this.beforeResizeWidth = pre.clientWidth;
544        event.stopPropagation();
545      }
546    });
547    element.addEventListener('click', (event) => {
548      event.stopPropagation();
549    });
550  }
551
552  resizeMouseMoveEventHandler(header: HTMLDivElement) {
553    header.addEventListener('mousemove', (event) => {
554      if (!this.columnResizeEnable) return;
555      if (this.isResize) {
556        let width = event.clientX - this.resizeDownX;
557        header.style.cursor = 'col-resize';
558        let preWidth = Math.max(this.beforeResizeWidth + width, this.columnMinWidth);
559        for (let i = 0; i < header.childNodes.length; i++) {
560          let node = header.childNodes.item(i) as HTMLDivElement;
561          this.gridTemplateColumns[i] = `${node.clientWidth}px`;
562        }
563        this.gridTemplateColumns[this.resizeColumnIndex - 1] = `${preWidth}px`;
564        header.style.gridTemplateColumns = this.gridTemplateColumns.join(' ');
565        let preNode = header.childNodes.item(this.resizeColumnIndex - 1) as HTMLDivElement;
566        preNode.style.width = `${preWidth}px`;
567        this.shadowRoot!.querySelectorAll<HTMLDivElement>('.tr').forEach((tr) => {
568          if (this.hasAttribute('tree')) {
569            tr.style.gridTemplateColumns = this.gridTemplateColumns.slice(1).join(' ');
570          } else {
571            tr.style.gridTemplateColumns = this.gridTemplateColumns.join(' ');
572          }
573        });
574        event.preventDefault();
575        event.stopPropagation();
576      } else {
577        header.style.cursor = 'pointer';
578      }
579    });
580  }
581
582  adoptedCallback(): void {}
583
584  getCheckRows(): any[] {
585    // @ts-ignore
586    return [...this.shadowRoot!.querySelectorAll('div[class=tr][checked]')]
587      .map((a) => (a as any).data)
588      .map((a) => {
589        delete a['children'];
590        return a;
591      });
592  }
593
594  deleteRowsCondition(fn: any): void {
595    this.shadowRoot!.querySelectorAll('div[class=tr]').forEach((tr) => {
596      // @ts-ignore
597      if (fn(tr.data)) {
598        tr.remove();
599      }
600    });
601  }
602
603  meauseElementHeight(rowData: any): number {
604    return 27;
605  }
606
607  meauseTreeElementHeight(rowData: any, depth: number): number {
608    return 27;
609  }
610
611  getVisibleObjects(list: any[]) {
612    let headHeight = 0;
613    let totalHeight = headHeight;
614    let visibleObjects: TableRowObject[] = [];
615    let itemHandler = (rowData: any, index: number) => {
616      let height = this.meauseElementHeight(rowData);
617      let tableRowObject = new TableRowObject();
618      tableRowObject.height = height;
619      tableRowObject.top = totalHeight;
620      tableRowObject.data = rowData;
621      tableRowObject.rowIndex = index;
622      if (
623        Math.max(totalHeight, this.tableElement!.scrollTop + headHeight) <=
624        Math.min(totalHeight + height, this.tableElement!.scrollTop + this.tableElement!.clientHeight + headHeight)
625      ) {
626        let newTableElement = this.addTableElement(tableRowObject, false,false, true, totalHeight);
627        let td = newTableElement?.querySelectorAll('.td');
628        if (tableRowObject.data.rowName === 'cpu-profiler') {
629          td[0].innerHTML = '';
630          this.createTextColor(tableRowObject, td[0]);
631        }
632      }
633      totalHeight += height;
634      visibleObjects.push(tableRowObject);
635    };
636    let realIndex = 0;
637    list.forEach((item, index) => {
638      if (Array.isArray(item)) {
639        item.forEach((rowData, childIndex) => {
640          itemHandler(rowData, realIndex);
641          realIndex++;
642        });
643      } else {
644        itemHandler(item, index);
645      }
646    });
647    return { visibleObjects, totalHeight };
648  }
649
650  meauseAllRowHeight(list: any[]): TableRowObject[] {
651    this.tbodyElement!.innerHTML = '';
652    this.meauseRowElement = undefined;
653    let head = this.shadowRoot!.querySelector('.th');
654    this.tbodyElement && (this.tbodyElement.style.width = head?.clientWidth + 'px');
655    this.currentRecycleList = [];
656    let { visibleObjects, totalHeight } = this.getVisibleObjects(list);
657    this.tbodyElement && (this.tbodyElement.style.height = totalHeight + (this.isScrollXOutSide ? 0 : 0) + 'px');
658    this.tableElement &&
659      (this.tableElement.onscroll = (event) => {
660        let tblScrollTop = this.tableElement!.scrollTop;
661        let skip = 0;
662        for (let i = 0; i < visibleObjects.length; i++) {
663          if (
664            visibleObjects[i].top <= tblScrollTop &&
665            visibleObjects[i].top + visibleObjects[i].height >= tblScrollTop
666          ) {
667            skip = i;
668            break;
669          }
670        }
671        let reduce = this.currentRecycleList.map((item) => item.clientHeight).reduce((a, b) => a + b, 0);
672        if (reduce == 0) {
673          return;
674        }
675        while (reduce <= this.tableElement!.clientHeight) {
676          let newTableElement = this.addTableElement(visibleObjects[skip], false,false, false);
677          reduce += newTableElement.clientHeight;
678        }
679        for (let i = 0; i < this.currentRecycleList.length; i++) {
680          this.freshLineHandler(i, skip, visibleObjects);
681        }
682      });
683    return visibleObjects;
684  }
685
686  freshLineHandler(index: number, skip: number, visibleObjects: TableRowObject[]){
687    this.freshCurrentLine(this.currentRecycleList[index], visibleObjects[index + skip]);
688    if (visibleObjects[index + skip]) {
689      if (visibleObjects[index + skip].data.rowName === 'cpu-profiler') {
690        this.createTextColor(visibleObjects[index + skip], this.currentRecycleList[index].childNodes[0]);
691      }
692    }
693  }
694
695  newTableRowObject(item: any, totalHeight: number, depth: number, parentNode?: TableRowObject): TableRowObject {
696    let tableRowObject = new TableRowObject();
697    tableRowObject.depth = depth;
698    tableRowObject.data = item;
699    tableRowObject.top = totalHeight;
700    tableRowObject.height = this.meauseTreeElementHeight(tableRowObject, depth);
701    if (parentNode) {
702      parentNode!.children.push(tableRowObject);
703    }
704    return tableRowObject;
705  }
706
707  resetAllHeight(
708    list: any[],
709    depth: number,
710    totalHeight: number,
711    visibleObjects: TableRowObject[],
712    parentNode?: TableRowObject,
713    form?: RedrawTreeForm
714  ) {
715    let headHeight = this.theadElement?.clientHeight || 0;
716    list.forEach((item) => {
717      let tableRowObject = this.newTableRowObject(item, totalHeight, depth, parentNode);
718      if (this._mode === TableMode.Expand && form === RedrawTreeForm.Retract && !item.status) {
719        tableRowObject.expanded = false;
720      } else if (this._mode === TableMode.Expand && form === RedrawTreeForm.Default) {
721        tableRowObject.expanded = true;
722      }
723      if (
724        (this._mode === TableMode.Retract && !item.status) ||
725        (this._mode === TableMode.Expand && !item.status && form !== RedrawTreeForm.Expand)
726      ) {
727        tableRowObject.expanded = false;
728        if (item.children != undefined && item.children.length > 0) {
729          this.newTableRowObject(item, totalHeight, depth, tableRowObject);
730        }
731      }
732      if (
733        Math.max(totalHeight, this.tableElement!.scrollTop) <=
734        Math.min(
735          totalHeight + tableRowObject.height,
736          this.tableElement!.scrollTop + this.tableElement!.clientHeight - headHeight
737        )
738      ) {
739        this.addTableElement(tableRowObject,true, false, true, totalHeight);
740      }
741      totalHeight += tableRowObject.height;
742      visibleObjects.push(tableRowObject);
743      this.resetAllHeightChildrenHandler(item, depth, totalHeight, visibleObjects, tableRowObject, form);
744    });
745  }
746
747  resetAllHeightChildrenHandler(
748    item: any,
749    depth: number,
750    totalHeight: number,
751    visibleObjects: TableRowObject[],
752    tableRowObject?: TableRowObject,
753    form?: RedrawTreeForm
754  ) {
755    if (item.hasNext) {
756      // js memory的表格
757      if (item.parents != undefined && item.parents.length > 0 && item.status) {
758        this.resetAllHeight(item.parents, depth + 1, totalHeight, visibleObjects, tableRowObject);
759      } else if (item.children != undefined && item.children.length > 0 && item.status) {
760        this.resetAllHeight(item.children, depth + 1, totalHeight, visibleObjects, tableRowObject);
761      }
762    } else {
763      // 其他数据
764      if (
765        item.children != undefined &&
766        item.children.length > 0 &&
767        form === RedrawTreeForm.Expand &&
768        this._mode === TableMode.Expand
769      ) {
770        item.status = true;
771        this.resetAllHeight(item.children, depth + 1, totalHeight, visibleObjects, tableRowObject);
772      } else if (item.children != undefined && item.children.length > 0 && item.status) {
773        this.resetAllHeight(item.children, depth + 1, totalHeight, visibleObjects, tableRowObject);
774      }
775    }
776  }
777
778  measureReset(): void {
779    this.meauseRowElement = undefined;
780    this.tbodyElement!.innerHTML = '';
781    this.treeElement!.innerHTML = '';
782    this.currentRecycleList = [];
783    this.currentTreeDivList = [];
784  }
785
786  meauseTreeRowElement(list: any[], form?: RedrawTreeForm): TableRowObject[] {
787    this.measureReset();
788    let visibleObjects: TableRowObject[] = [];
789    let totalHeight = 0;
790    this.resetAllHeight(list,0, totalHeight, visibleObjects);
791    this.tbodyElement && (this.tbodyElement.style.height = totalHeight + 'px');
792    this.treeElement!.style.height = this.tableElement!.clientHeight - this.theadElement!.clientHeight + 'px';
793    this.tableElement &&
794      (this.tableElement.onscroll = (event) => {
795        let visibleObjects = this.recycleDs.filter((item) => {
796          return !item.rowHidden;
797        });
798        let top = this.tableElement!.scrollTop;
799        this.treeElement!.style.transform = `translateY(${top}px)`;
800        let skip = 0;
801        for (let index = 0; index < visibleObjects.length; index++) {
802          if (visibleObjects[index].top <= top && visibleObjects[index].top + visibleObjects[index].height >= top) {
803            skip = index;
804            break;
805          }
806        }
807        // 如果滚动高度大于数据全部收起的高度,并且this.currentRecycleList数组长度为0要给this.currentRecycleList赋值,不然tab页没有数据
808        if (
809          visibleObjects[0] &&
810          this.tableElement!.scrollTop >= this.value.length * visibleObjects[0].height &&
811          this.currentRecycleList.length === 0
812        ) {
813          this.addTableElement(visibleObjects[skip], true, false, false);
814        }
815        let reduce = this.currentRecycleList.map((item) => item.clientHeight).reduce((a, b) => a + b, 0);
816        if (reduce == 0) {
817          return;
818        }
819        while (reduce <= this.tableElement!.clientHeight) {
820          let newTableElement = this.addTableElement(visibleObjects[skip], true, false, false);
821          reduce += newTableElement.clientHeight;
822        }
823        for (let i = 0; i < this.currentRecycleList.length; i++) {
824          this.freshCurrentLine(
825            this.currentRecycleList[i],
826            visibleObjects[i + skip],
827            this.treeElement?.children[i] as HTMLElement
828          );
829        }
830      });
831    return visibleObjects;
832  }
833
834  private addTableElement(
835    rowData: TableRowObject,
836    isTree: boolean,
837    last: boolean,
838    translate: boolean,
839    totalHeight?: number
840  ) {
841    let newTableElement;
842    if (isTree) {
843      newTableElement = this.createNewTreeTableElement(rowData);
844    } else {
845      newTableElement = this.createNewTableElement(rowData);
846    }
847    if (translate) {
848      newTableElement.style.transform = `translateY(${totalHeight}px)`;
849    }
850    this.tbodyElement?.append(newTableElement);
851    if (last) {
852      if (this.hasAttribute('tree')) {
853        if (this.treeElement?.lastChild) {
854          (this.treeElement?.lastChild as HTMLElement).style.height = rowData.height + 'px';
855        }
856      }
857    }
858    this.currentRecycleList.push(newTableElement);
859    return newTableElement;
860  }
861
862  createNewTreeTableElement(rowData: TableRowObject): any {
863    let rowTreeElement = document.createElement('div');
864    rowTreeElement.classList.add('tr');
865    let treeTop = 0;
866    if (this.treeElement!.children?.length > 0) {
867      let transX = Number((this.treeElement?.lastChild as HTMLElement).style.transform.replace(/[^0-9]/gi, ''));
868      treeTop += transX + rowData.height;
869    }
870    this?.columns?.forEach((column: any, index) => {
871      let dataIndex = column.getAttribute('data-index') || '1';
872      let td: any;
873      if (index === 0) {
874        td = this.firstElementTdHandler(rowTreeElement, dataIndex, rowData, column);
875      } else {
876        td = this.otherElementHandler(dataIndex, rowData, column);
877        rowTreeElement.append(td);
878      }
879    });
880    let lastChild = this.treeElement?.lastChild as HTMLElement;
881    if (lastChild) {
882      lastChild.style.transform = `translateY(${treeTop}px)`;
883    }
884    (rowTreeElement as any).data = rowData.data;
885    rowTreeElement.style.gridTemplateColumns = this.gridTemplateColumns.slice(1).join(' ');
886    rowTreeElement.style.position = 'absolute';
887    rowTreeElement.style.top = '0px';
888    rowTreeElement.style.left = '0px';
889    rowTreeElement.style.cursor = 'pointer';
890    this.setHighLight(rowData.data.isSearch, rowTreeElement);
891    this.addRowElementEvent(rowTreeElement, rowData);
892    return rowTreeElement;
893  }
894
895  addRowElementEvent(rowTreeElement: HTMLDivElement, rowData: any): void {
896    rowTreeElement.onmouseenter = () => {
897      if ((rowTreeElement as any).data.isSelected) return;
898      let indexOf = this.currentRecycleList.indexOf(rowTreeElement);
899      this.currentTreeDivList.forEach((row) => {
900        row.classList.remove('mouse-in');
901      });
902      if (indexOf >= 0 && indexOf < this.treeElement!.children.length) {
903        this.setMouseIn(true, [this.treeElement?.children[indexOf] as HTMLElement]);
904      }
905    };
906    rowTreeElement.onmouseleave = () => {
907      if ((rowTreeElement as any).data.isSelected) return;
908      let indexOf = this.currentRecycleList.indexOf(rowTreeElement);
909      if (indexOf >= 0 && indexOf < this.treeElement!.children.length) {
910        this.setMouseIn(false, [this.treeElement?.children[indexOf] as HTMLElement]);
911      }
912    };
913    rowTreeElement.onmouseup = (e: MouseEvent) => {
914      let indexOf = this.currentRecycleList.indexOf(rowTreeElement);
915      this.dispatchRowClickEvent(rowData, [this.treeElement?.children[indexOf] as HTMLElement, rowTreeElement], e);
916    };
917  }
918
919  firstElementTdHandler(tr: HTMLDivElement, dataIndex: string, row: any, column: any) {
920    let td: any;
921    let text = formatName(dataIndex, row.data[dataIndex], this);
922    if (column.template) {
923      td = column.template.render(row.data).content.cloneNode(true);
924      td.template = column.template;
925      td.title = row.data[dataIndex];
926    } else {
927      td = document.createElement('div');
928      if (row.data.rowName === 'js-memory' || row.data.rowName === 'cpu-profiler') {
929        td.innerHTML = '';
930      } else {
931        td.innerHTML = text;
932      }
933      td.dataIndex = dataIndex;
934      if (text.indexOf('&lt;') === -1) {
935        td.title = text;
936      }
937    }
938    if (row.data.children && row.data.children.length > 0 && !row.data.hasNext) {
939      let btn = this.createExpandBtn(row);
940      td.insertBefore(btn, td.firstChild);
941    }
942    if (row.data.hasNext) {
943      td.title = row.data.objectName;
944      let btn = this.createBtn(row);
945      td.insertBefore(btn, td.firstChild);
946    }
947    td.style.paddingLeft = row.depth * iconWidth + 'px';
948    if (!row.data.children || row.data.children.length === 0) {
949      td.style.paddingLeft = iconWidth * row.depth + iconWidth + iconPadding * 2 + 'px';
950    }
951    this.jsMemoryHandler(row, td);
952    if (row.data.rowName === 'cpu-profiler') {
953      this.createTextColor(row, td);
954    }
955    (td as any).data = row.data;
956    td.classList.add('tree-first-body');
957    td.style.position = 'absolute';
958    td.style.top = '0px';
959    td.style.left = '0px';
960    this.addFirstElementEvent(td, tr, row);
961    this.setHighLight(row.data.isSearch, td);
962    this.treeElement!.style.width = column.getAttribute('width');
963    this.treeElement?.append(td);
964    this.currentTreeDivList.push(td);
965    return td;
966  }
967
968  addFirstElementEvent(td: HTMLDivElement, tr: HTMLDivElement, rowData: any): void {
969    td.onmouseenter = () => {
970      let indexOf = this.currentTreeDivList.indexOf(td);
971      this.currentRecycleList.forEach((row) => {
972        row.classList.remove('mouse-in');
973      });
974      if (indexOf >= 0 && indexOf < this.currentRecycleList.length && td.innerHTML != '') {
975        this.setMouseIn(true, [tr]);
976      }
977    };
978    td.onmouseleave = () => {
979      let indexOf = this.currentTreeDivList.indexOf(td);
980      if (indexOf >= 0 && indexOf < this.currentRecycleList.length) {
981        this.setMouseIn(false, [tr]);
982      }
983    };
984    td.onmouseup = (e: MouseEvent) => {
985      let indexOf = this.currentTreeDivList.indexOf(td);
986      this.dispatchRowClickEvent(rowData, [td, tr], e);
987    };
988  }
989
990  otherElementHandler(dataIndex: string, rowData: any, column: any) {
991    let tdDiv: any = document.createElement('div');
992    tdDiv.classList.add('td');
993    tdDiv.style.overflow = 'hidden';
994    tdDiv.style.textOverflow = 'ellipsis';
995    tdDiv.style.whiteSpace = 'nowrap';
996    let text = formatName(dataIndex, rowData.data[dataIndex], this);
997    if (text.indexOf('&lt;') === -1) {
998      if (dataIndex === 'selfTimeStr' && rowData.data.chartFrameChildren) {
999        tdDiv.title = rowData.data.selfTime + 'ns';
1000      } else if (dataIndex === 'totalTimeStr' && rowData.data.chartFrameChildren) {
1001        tdDiv.title = rowData.data.totalTime + 'ns';
1002      } else {
1003        tdDiv.title = text;
1004      }
1005    }
1006    tdDiv.dataIndex = dataIndex;
1007    tdDiv.style.justifyContent = column.getAttribute('align') || 'flex-start';
1008    if (column.template) {
1009      tdDiv.appendChild(column.template.render(rowData.data).content.cloneNode(true));
1010      tdDiv.template = column.template;
1011    } else {
1012      tdDiv.innerHTML = text;
1013    }
1014    return tdDiv;
1015  }
1016
1017  createNewTableElement(rowData: any): any {
1018    let newTableElement = document.createElement('div');
1019    newTableElement.classList.add('tr');
1020    this?.columns?.forEach((column: any) => {
1021      let dataIndex = column.getAttribute('data-index') || '1';
1022      let td = this.createColumnTd(dataIndex, column, rowData);
1023      newTableElement.append(td);
1024    });
1025    newTableElement.onmouseup = (e: MouseEvent) => {
1026      this.dispatchRowClickEvent(rowData, [newTableElement], e);
1027    };
1028    newTableElement.onmouseenter = () => {
1029      this.dispatchRowHoverEvent(rowData, [newTableElement]);
1030    };
1031    if (rowData.data.isSelected != undefined) {
1032      this.setSelectedRow(rowData.data.isSelected, [newTableElement]);
1033    }
1034    (newTableElement as any).data = rowData.data;
1035    newTableElement.style.cursor = 'pointer';
1036    newTableElement.style.gridTemplateColumns = this.gridTemplateColumns.join(' ');
1037    newTableElement.style.position = 'absolute';
1038    newTableElement.style.top = '0px';
1039    newTableElement.style.left = '0px';
1040    if (this.getItemTextColor) {
1041      newTableElement.style.color = this.getItemTextColor(rowData.data);
1042    }
1043    return newTableElement;
1044  }
1045
1046  createColumnTd(dataIndex: string, column: any, rowData: any): any {
1047    let td: any;
1048    td = document.createElement('div');
1049    td.classList.add('td');
1050    td.style.overflow = 'hidden';
1051    td.style.textOverflow = 'ellipsis';
1052    td.style.whiteSpace = 'nowrap';
1053    td.dataIndex = dataIndex;
1054    td.style.justifyContent = column.getAttribute('align') || 'flex-start';
1055    let text = formatName(dataIndex, rowData.data[dataIndex], this);
1056    if (text.indexOf('&lt;') === -1) {
1057      if (dataIndex === 'totalTimeStr' && rowData.data.chartFrameChildren) {
1058        td.title = rowData.data.totalTime + 'ns';
1059      } else {
1060        td.title = text;
1061      }
1062    }
1063    //   如果表格中有模板的情况,将模板中的数据放进td中,没有模板,直接将文本放进td
1064    //  但是对于Current Selection tab页来说,表格前两列是时间,第三列是input标签,第四列是button标签
1065    //  而第一行的数据只有第四列一个button,和模板中的数据并不一样,所以要特别处理一下
1066    if (column.template) {
1067      if (dataIndex === 'color' && rowData.data.colorEl === undefined) {
1068        td.innerHTML = '';
1069        td.template = '';
1070      } else if (dataIndex === 'operate' && rowData.data.operate && rowData.data.operate.innerHTML === 'RemoveAll') {
1071        let removeAll = document.createElement('button');
1072        removeAll.className = 'removeAll';
1073        removeAll.innerHTML = 'RemoveAll';
1074        removeAll.style.background = 'var(--dark-border1,#262f3c)';
1075        removeAll.style.color = 'white';
1076        removeAll.style.borderRadius = '10px';
1077        removeAll.style.fontSize = '10px';
1078        removeAll.style.height = '18px';
1079        removeAll.style.lineHeight = '18px';
1080        removeAll.style.minWidth = '7em';
1081        removeAll.style.border = 'none';
1082        removeAll.style.cursor = 'pointer';
1083        removeAll.style.outline = 'inherit';
1084        td.appendChild(removeAll);
1085      } else {
1086        td.appendChild(column.template.render(rowData.data).content.cloneNode(true));
1087        td.template = column.template;
1088      }
1089    } else {
1090      td.innerHTML = text;
1091    }
1092    return td;
1093  }
1094
1095  createBtn(rowData: any): any {
1096    let btn: any = document.createElement('lit-icon');
1097    btn.classList.add('tree-icon');
1098    if (rowData.data.expanded) {
1099      btn.name = 'plus-square';
1100    } else {
1101      btn.name = 'minus-square';
1102    }
1103    btn.addEventListener('mouseup', (e: MouseEvent) => {
1104      if (e.button === 0) {
1105        rowData.data.status = false;
1106        const resetNodeHidden = (hidden: boolean, rowData: any) => {
1107          if (hidden) {
1108            rowData.children.forEach((child: any) => {
1109              child.rowHidden = false;
1110            });
1111          } else {
1112            rowData.children.forEach((child: any) => {
1113              child.rowHidden = true;
1114              resetNodeHidden(hidden, child);
1115            });
1116          }
1117        };
1118
1119        if (rowData.data.expanded) {
1120          rowData.data.status = true;
1121          this.dispatchRowClickEventIcon(rowData, [btn]);
1122          rowData.data.expanded = false;
1123          resetNodeHidden(true, rowData);
1124        } else {
1125          rowData.data.expanded = true;
1126          rowData.data.status = false;
1127          resetNodeHidden(false, rowData);
1128        }
1129        this.reMeauseHeight();
1130      }
1131      e.stopPropagation();
1132    });
1133    return btn;
1134  }
1135
1136  resetExpandNodeHidden = (hidden: boolean, rowData: any) => {
1137    if (rowData.children.length > 0) {
1138      if (hidden) {
1139        rowData.children.forEach((child: any) => {
1140          child.rowHidden = true;
1141          this.resetExpandNodeHidden(hidden, child);
1142        });
1143      } else {
1144        rowData.children.forEach((child: any) => {
1145          child.rowHidden = !rowData.expanded;
1146          if (rowData.expanded) {
1147            this.resetExpandNodeHidden(hidden, child);
1148          }
1149        });
1150      }
1151    }
1152  };
1153
1154  setChildrenStatus(rowData:any, data: any) {
1155    for (let d of data) {
1156      if (rowData.data === d) {
1157        d.status = false;
1158      }
1159      if (d.children != undefined && d.children.length > 0) {
1160        this.setChildrenStatus(rowData, d.children);
1161      }
1162    }
1163  }
1164  createExpandBtn(rowData: any): any {
1165    let btn: any = document.createElement('lit-icon');
1166    btn.classList.add('tree-icon');
1167    // @ts-ignore
1168    if (rowData.expanded) {
1169      btn.name = 'minus-square';
1170    } else {
1171      btn.name = 'plus-square';
1172    }
1173    btn.onmouseup = (e: MouseEvent) => {
1174      if (e.button === 0) {
1175        if (rowData.expanded && this._mode === TableMode.Retract) {
1176          rowData.data.status = false;
1177          rowData.expanded = false;
1178          this.resetExpandNodeHidden(true, rowData);
1179        } else if (!rowData.expanded && this._mode === TableMode.Retract) {
1180          rowData.expanded = true;
1181          rowData.data.status = true;
1182          this.recycleDs = this.meauseTreeRowElement(this.value, RedrawTreeForm.Retract);
1183          this.resetExpandNodeHidden(false, rowData);
1184        }
1185        if (this._mode === TableMode.Expand && rowData.expanded) {
1186          // 点击收起的时候将点击的那条数据的status改为false
1187          this.setChildrenStatus(rowData, this.value);
1188          rowData.expanded = false;
1189          this.resetExpandNodeHidden(true, rowData);
1190        } else if (this._mode === TableMode.Expand && !rowData.expanded) {
1191          if (rowData.data.children) {
1192            rowData.data.status = true;
1193          }
1194          this.recycleDs = this.meauseTreeRowElement(this.value, RedrawTreeForm.Default);
1195          rowData.expanded = true;
1196          this.resetExpandNodeHidden(false, rowData);
1197        }
1198        this.reMeauseHeight();
1199      }
1200      e.stopPropagation();
1201    };
1202    return btn;
1203  }
1204
1205  reMeauseHeight(): void {
1206    if (this.currentRecycleList.length === 0) {
1207      return;
1208    }
1209    let totalHeight = 0;
1210    this.recycleDs.forEach((it) => {
1211      if (!it.rowHidden) {
1212        it.top = totalHeight;
1213        totalHeight += it.height;
1214      }
1215    });
1216    this.tbodyElement && (this.tbodyElement.style.height = totalHeight + (this.isScrollXOutSide ? 0 : 0) + 'px');
1217    this.treeElement!.style.height = this.tableElement!.clientHeight - this.theadElement!.clientHeight + 'px';
1218    let visibleObjects = this.recycleDs.filter((item) => {
1219      return !item.rowHidden;
1220    });
1221    let top = this.tableElement!.scrollTop;
1222    let skip = 0;
1223    for (let i = 0; i < visibleObjects.length; i++) {
1224      if (visibleObjects[i].top <= top && visibleObjects[i].top + visibleObjects[i].height >= top) {
1225        skip = i;
1226        break;
1227      }
1228    }
1229    let reduce = this.currentRecycleList.map((item) => item.clientHeight).reduce((a, b) => a + b, 0);
1230    if (reduce === 0) {
1231      return;
1232    }
1233    while (reduce <= this.tableElement!.clientHeight + 1) {
1234      let isTree = this.hasAttribute('tree');
1235      let newTableElement = this.addTableElement(visibleObjects[skip], isTree, isTree, false);
1236      reduce += newTableElement.clientHeight;
1237    }
1238    for (let i = 0; i < this.currentRecycleList.length; i++) {
1239      if (this.hasAttribute('tree')) {
1240        this.freshCurrentLine(
1241          this.currentRecycleList[i],
1242          visibleObjects[i + skip],
1243          this.treeElement?.children[i] as HTMLElement
1244        );
1245      } else {
1246        this.freshLineHandler(i, skip, visibleObjects);
1247      }
1248    }
1249  }
1250
1251  getWheelStatus(element: any): void {
1252    element.addEventListener('wheel', (event: WheelEvent) => {
1253      if (element.scrollWidth !== element.offsetWidth) {
1254        event.preventDefault();
1255      }
1256      element.scrollLeft += event.deltaY;
1257    });
1258  }
1259
1260  renderTable(): void {
1261    if (!this.columns) {
1262      return;
1263    }
1264    if (!this.ds) {
1265      return;
1266    } // If no data source is set, it is returned directly
1267    this.normalDs = [];
1268    this.tbodyElement!.innerHTML = ''; // Clear the table contents
1269    this.ds.forEach((rowData: any) => {
1270      let tblRowElement = document.createElement('div');
1271      tblRowElement.classList.add('tr');
1272      // @ts-ignore
1273      tblRowElement.data = rowData;
1274      let gridTemplateColumns: Array<any> = [];
1275      // If the table is configured with selectable (select row mode) add a checkbox at the head of the line alone
1276      this.renderTableRowSelect(tblRowElement);
1277      this.tableColumns!.forEach((tblColumn) => {
1278        tblColumn.addEventListener('contextmenu', (e) => {
1279          e.preventDefault();
1280        });
1281        let dataIndex = tblColumn.getAttribute('data-index') || '1';
1282        gridTemplateColumns.push(tblColumn.getAttribute('width') || '1fr');
1283        this.renderTableRowColumnElement(tblColumn, tblRowElement, dataIndex, rowData);
1284      });
1285      if (this.selectable) {
1286        // If the table with selection is preceded by a 60px column
1287        tblRowElement.style.gridTemplateColumns = '60px ' + gridTemplateColumns.join(' ');
1288      } else {
1289        tblRowElement.style.gridTemplateColumns = gridTemplateColumns.join(' ');
1290      }
1291      this.renderTableRowElementEvent(tblRowElement, rowData);
1292      this.normalDs.push(tblRowElement);
1293      this.tbodyElement!.append(tblRowElement);
1294    });
1295  }
1296
1297  renderTableRowSelect(tblRowElement: HTMLDivElement): void {
1298    if (this.selectable) {
1299      let tblBox = document.createElement('div');
1300      tblBox.style.display = 'flex';
1301      tblBox.style.justifyContent = 'center';
1302      tblBox.style.alignItems = 'center';
1303      tblBox.classList.add('td');
1304      let checkbox = document.createElement('lit-checkbox');
1305      checkbox.classList.add('row-checkbox');
1306      checkbox.onchange = (e: any) => {
1307        // Checkbox checking affects whether the div corresponding to the row has a checked attribute for marking
1308        if (e.detail.checked) {
1309          tblRowElement.setAttribute('checked', '');
1310        } else {
1311          tblRowElement.removeAttribute('checked');
1312        }
1313      };
1314      this.getWheelStatus(tblBox);
1315      tblBox.appendChild(checkbox);
1316      tblRowElement.appendChild(tblBox);
1317    }
1318  }
1319
1320  renderTableRowColumnElement(tblColumn: LitTableColumn, tblRowElement: HTMLDivElement, dataIndex: string, rowData: any): void {
1321    if (tblColumn.template) {
1322      // If you customize the rendering, you get the nodes from the template
1323      // @ts-ignore
1324      let cloneNode = tblColumn.template.render(rowData).content.cloneNode(true);
1325      let tblCustomDiv = document.createElement('div');
1326      tblCustomDiv.classList.add('td');
1327      tblCustomDiv.style.wordBreak = 'break-all';
1328      tblCustomDiv.style.whiteSpace = 'pre-wrap';
1329      tblCustomDiv.style.justifyContent = tblColumn.getAttribute('align') || '';
1330      if (tblColumn.hasAttribute('fixed')) {
1331        fixed(tblCustomDiv, tblColumn.getAttribute('fixed') || '', '#ffffff');
1332      }
1333      this.getWheelStatus(tblCustomDiv);
1334      tblCustomDiv.append(cloneNode);
1335      tblRowElement.append(tblCustomDiv);
1336    } else {
1337      let tblDiv = document.createElement('div');
1338      tblDiv.classList.add('td');
1339      tblDiv.style.wordBreak = 'break-all';
1340      tblDiv.style.whiteSpace = 'pre-wrap';
1341      tblDiv.title = rowData[dataIndex];
1342      tblDiv.style.justifyContent = tblColumn.getAttribute('align') || '';
1343      if (tblColumn.hasAttribute('fixed')) {
1344        fixed(tblDiv, tblColumn.getAttribute('fixed') || '', '#ffffff');
1345      }
1346      this.getWheelStatus(tblDiv);
1347      tblDiv.innerHTML = formatName(dataIndex, rowData[dataIndex], this);
1348      tblRowElement.append(tblDiv);
1349    }
1350  }
1351
1352  renderTableRowElementEvent(tblRowElement: HTMLDivElement, rowData: any): void {
1353    tblRowElement.onmouseup = (e: MouseEvent) => {
1354      this.dispatchEvent(
1355        new CustomEvent('row-click', {
1356          detail: {
1357            rowData,
1358            data: rowData,
1359            callBack: (isSelected: boolean) => {
1360              //是否爲单选
1361              if (isSelected) {
1362                this.clearAllSelection(rowData);
1363              }
1364              this.setSelectedRow(rowData.isSelected, [tblRowElement]);
1365            },
1366          },
1367          composed: true,
1368        })
1369      );
1370    };
1371  }
1372
1373  freshCurrentLine(element: HTMLElement, rowObject: TableRowObject, firstElement?: HTMLElement): void {
1374    if (!rowObject) {
1375      if (firstElement) {
1376        firstElement.style.display = 'none';
1377      }
1378      element.style.display = 'none';
1379      return;
1380    }
1381    let childIndex = -1;
1382    this.setHighLight(rowObject.data.isSearch, element);
1383    element.childNodes.forEach((child) => {
1384      if (child.nodeType != 1) return;
1385      childIndex++;
1386      let idx = firstElement !== undefined ? childIndex + 1 : childIndex;
1387      this.freshLineFirstElementHandler(firstElement, rowObject, childIndex);
1388      if (idx < this.columns!.length) {
1389        let dataIndex = this.columns![idx].getAttribute('data-index') || '1';
1390        let text = formatName(dataIndex, rowObject.data[dataIndex], this);
1391        if ((this.columns![idx] as any).template) {
1392          (child as HTMLElement).innerHTML = '';
1393          (child as HTMLElement).appendChild(
1394            (this.columns![idx] as any).template.render(rowObject.data).content.cloneNode(true)
1395          );
1396          (child as HTMLElement).title = text;
1397        } else {
1398          (child as HTMLElement).innerHTML = text;
1399          if (dataIndex === 'selfTimeStr' && rowObject.data.chartFrameChildren) {
1400            (child as HTMLElement).title = rowObject.data.selfTime + 'ns';
1401          } else if (dataIndex === 'totalTimeStr' && rowObject.data.chartFrameChildren) {
1402            (child as HTMLElement).title = rowObject.data.totalTime + 'ns';
1403          } else if (dataIndex === 'timeStr' && rowObject.data instanceof JsCpuProfilerStatisticsStruct) {
1404            (child as HTMLElement).title = rowObject.data.time + 'ns';
1405          } else {
1406            (child as HTMLElement).title = text;
1407          }
1408        }
1409      }
1410    });
1411    this.freshLineStyleAndEvents(element, rowObject, firstElement);
1412  }
1413
1414  freshLineStyleAndEvents(element: HTMLElement, rowObject: TableRowObject, firstElement?: HTMLElement): void {
1415    if (element.style.display === 'none') {
1416      element.style.display = 'grid';
1417    }
1418    element.style.transform = `translateY(${rowObject.top}px)`;
1419    if (firstElement && firstElement.style.display === 'none') {
1420      firstElement.style.display = 'flex';
1421    }
1422    element.onmouseup = (e: MouseEvent) => {
1423      if (firstElement !== undefined) {
1424        this.dispatchRowClickEvent(rowObject, [firstElement, element], e);
1425      } else {
1426        this.dispatchRowClickEvent(rowObject, [element], e);
1427      }
1428    };
1429    element.onmouseenter = () => {
1430      this.dispatchRowHoverEvent(rowObject, [element]);
1431    };
1432    (element as any).data = rowObject.data;
1433    if (rowObject.data.isSelected !== undefined) {
1434      this.setSelectedRow(rowObject.data.isSelected, [element]);
1435    } else {
1436      this.setSelectedRow(false, [element]);
1437    }
1438    if (rowObject.data.isHover !== undefined) {
1439      this.setMouseIn(rowObject.data.isHover, [element]);
1440    } else {
1441      this.setMouseIn(false, [element]);
1442    }
1443    if (this.getItemTextColor) {
1444      element.style.color = this.getItemTextColor((element as any).data);
1445    }
1446  }
1447
1448  freshLineFirstElementHandler(firstElement: any, rowObject: TableRowObject, childIndex: number): void {
1449    if (firstElement !== undefined && childIndex === 0) {
1450      this.setHighLight(rowObject.data.isSearch, firstElement);
1451      (firstElement as any).data = rowObject.data;
1452      if ((this.columns![0] as any).template) {
1453        firstElement.innerHTML = (this.columns![0] as any).template
1454          .render(rowObject.data)
1455          .content.cloneNode(true).innerHTML;
1456      } else {
1457        let dataIndex = this.columns![0].getAttribute('data-index') || '1';
1458        let text = formatName(dataIndex, rowObject.data[dataIndex], this);
1459        if (rowObject.data.rowName === 'js-memory' || rowObject.data.rowName === 'cpu-profiler') {
1460          firstElement.innerHTML = '';
1461        } else {
1462          firstElement.innerHTML = text;
1463        }
1464        firstElement.title = text;
1465      }
1466      if (rowObject.children && rowObject.children.length > 0 && !rowObject.data.hasNext) {
1467        let btn = this.createExpandBtn(rowObject);
1468        firstElement.insertBefore(btn, firstElement.firstChild);
1469      }
1470      firstElement.style.paddingLeft = iconWidth * rowObject.depth + 'px';
1471      if (!rowObject.children || rowObject.children.length === 0) {
1472        firstElement.style.paddingLeft = iconWidth * rowObject.depth + iconWidth + iconPadding * 2 + 'px';
1473      }
1474      if (rowObject.data.hasNext) {
1475        let btn = this.createBtn(rowObject);
1476        firstElement.title = rowObject.data.objectName;
1477        firstElement.insertBefore(btn, firstElement.firstChild);
1478        firstElement.style.paddingLeft = iconWidth * rowObject.depth + 'px';
1479      }
1480      this.jsMemoryHandler(rowObject, firstElement);
1481      if (rowObject.data.rowName === 'cpu-profiler') {
1482        this.createTextColor(rowObject, firstElement);
1483      }
1484      firstElement.onmouseup = (e: MouseEvent) => {
1485        this.dispatchRowClickEvent(rowObject, [firstElement, element], e);
1486      };
1487      firstElement.style.transform = `translateY(${rowObject.top - this.tableElement!.scrollTop}px)`;
1488      if (rowObject.data.isSelected !== undefined) {
1489        this.setSelectedRow(rowObject.data.isSelected, [firstElement]);
1490      } else {
1491        this.setSelectedRow(false, [firstElement]);
1492      }
1493    }
1494  }
1495
1496  setSelectedRow(isSelected: boolean, rows: any[]): void {
1497    if (isSelected) {
1498      rows.forEach((row) => {
1499        if (row.classList.contains('mouse-in')) {
1500          row.classList.remove('mouse-in');
1501        }
1502        row.classList.add('mouse-select');
1503      });
1504    } else {
1505      rows.forEach((row) => {
1506        row.classList.remove('mouse-select');
1507      });
1508    }
1509  }
1510
1511  setMouseIn(isMouseIn: boolean, rows: any[]): void {
1512    if (isMouseIn) {
1513      rows.forEach((row) => {
1514        row.classList.add('mouse-in');
1515      });
1516    } else {
1517      rows.forEach((row) => {
1518        row.classList.remove('mouse-in');
1519      });
1520    }
1521  }
1522
1523  scrollToData(data: any): void {
1524    if (this.isRecycleList) {
1525      if (this.recycleDs.length > 0) {
1526        let filter = this.recycleDs.filter((item) => {
1527          return item.data === data;
1528        });
1529        if (filter.length > 0) {
1530          this.tableElement!.scrollTop = filter[0].top;
1531        }
1532        this.setCurrentSelection(data);
1533      }
1534    } else {
1535      if (this.normalDs.length > 0) {
1536        let filter = this.normalDs.filter((item) => {
1537          return item.data === data;
1538        });
1539        if (filter.length > 0) {
1540          this.tableElement!.scrollTop = filter[0].top;
1541        }
1542      }
1543    }
1544  }
1545
1546  expandList(datasource: any[]): void {
1547    let filter = this.recycleDs.filter((item) => {
1548      return datasource.indexOf(item.data) != -1;
1549    });
1550    if (filter.length > 0) {
1551      filter.forEach((item) => {
1552        item.expanded = true;
1553        item.rowHidden = false;
1554      });
1555    }
1556    this.reMeauseHeight();
1557  }
1558
1559  clearAllSelection(rowObjectData: any): void {
1560    if (this.isRecycleList) {
1561      this.recycleDs.forEach((item) => {
1562        if (item.data != rowObjectData && item.data.isSelected) {
1563          item.data.isSelected = false;
1564        }
1565      });
1566      this.setSelectedRow(false, this.currentTreeDivList);
1567      this.setSelectedRow(false, this.currentRecycleList);
1568    } else {
1569      this.dataSource.forEach((item) => {
1570        if (item != rowObjectData && item.isSelected) {
1571          item.isSelected = false;
1572        }
1573      });
1574      this.setSelectedRow(false, this.normalDs);
1575    }
1576  }
1577
1578  clearAllHover(rowObjectData: any): void {
1579    if (this.isRecycleList) {
1580      this.recycleDs.forEach((item) => {
1581        if (item.data != rowObjectData && item.data.isHover) {
1582          item.data.isHover = false;
1583        }
1584      });
1585      this.setMouseIn(false, this.currentTreeDivList);
1586      this.setMouseIn(false, this.currentRecycleList);
1587    } else {
1588      this.dataSource.forEach((item) => {
1589        if (item != rowObjectData && item.isHover) {
1590          item.isHover = false;
1591        }
1592      });
1593      this.setMouseIn(false, this.normalDs);
1594    }
1595  }
1596
1597  mouseOut(): void {
1598    if (this.isRecycleList) {
1599      this.recycleDs.forEach((item) => (item.data.isHover = false));
1600      this.setMouseIn(false, this.currentTreeDivList);
1601      this.setMouseIn(false, this.currentRecycleList);
1602    } else {
1603      this.dataSource.forEach((item) => (item.isHover = false));
1604      this.setMouseIn(false, this.normalDs);
1605    }
1606    this.dispatchEvent(
1607      new CustomEvent('row-hover', {
1608        detail: {
1609          data: undefined,
1610        },
1611        composed: true,
1612      })
1613    );
1614  }
1615
1616  setCurrentSelection(selectionData: any): void {
1617    if (this.isRecycleList) {
1618      if (selectionData.isSelected !== undefined) {
1619        this.currentTreeDivList.forEach((itemEl) => {
1620          if ((itemEl as any).data === selectionData) {
1621            this.setSelectedRow(selectionData.isSelected, [itemEl]);
1622          }
1623        });
1624        this.currentRecycleList.forEach((recycleItem) => {
1625          if ((recycleItem as any).data === selectionData) {
1626            this.setSelectedRow(selectionData.isSelected, [recycleItem]);
1627          }
1628        });
1629      }
1630    } else {
1631      if (selectionData.isSelected !== undefined) {
1632        this.normalDs.forEach((item) => {
1633          if ((item as any).data === selectionData) {
1634            this.setSelectedRow(selectionData.isSelected, [item]);
1635          }
1636        });
1637      }
1638    }
1639  }
1640
1641  setCurrentHover(data: any): void {
1642    if (this.isRecycleList) {
1643      this.setMouseIn(false, this.currentTreeDivList);
1644      this.setMouseIn(false, this.currentRecycleList);
1645      if (data.isHover !== undefined) {
1646        this.currentTreeDivList.forEach((hoverItem) => {
1647          if ((hoverItem as any).data === data) {
1648            this.setMouseIn(data.isHover, [hoverItem]);
1649          }
1650        });
1651        this.currentRecycleList.forEach((hoverItem) => {
1652          if ((hoverItem as any).data === data) {
1653            this.setMouseIn(data.isHover, [hoverItem]);
1654          }
1655        });
1656      }
1657    } else {
1658      this.setMouseIn(false, this.normalDs);
1659      if (data.isHover !== undefined) {
1660        this.normalDs.forEach((item): void => {
1661          if ((item as any).data === data) {
1662            this.setMouseIn(data.isHover, [item]);
1663          }
1664        });
1665      }
1666    }
1667  }
1668
1669  dispatchRowClickEventIcon(rowData: any, elements: any[]): void {
1670    this.dispatchEvent(
1671      new CustomEvent('icon-click', {
1672        detail: {
1673          ...rowData.data,
1674          data: rowData.data,
1675          callBack: (isSelected: boolean): void => {
1676            //是否爲单选
1677            if (isSelected) {
1678              this.clearAllSelection(rowData.data);
1679            }
1680            this.setSelectedRow(rowData.data.isSelected, elements);
1681          },
1682        },
1683        composed: true,
1684      })
1685    );
1686  }
1687
1688  dispatchRowClickEvent(rowObject: any, elements: any[], event: MouseEvent): void {
1689    this.dispatchEvent(
1690      new CustomEvent('row-click', {
1691        detail: {
1692          button: event.button,
1693          ...rowObject.data,
1694          data: rowObject.data,
1695          callBack: (isSelected: boolean): void => {
1696            //是否爲单选
1697            if (isSelected) {
1698              this.clearAllSelection(rowObject.data);
1699            }
1700            this.setSelectedRow(rowObject.data.isSelected, elements);
1701          },
1702        },
1703        composed: true,
1704      })
1705    );
1706  }
1707
1708  dispatchRowHoverEvent(rowObject: any, elements: any[]): void {
1709    this.dispatchEvent(
1710      new CustomEvent('row-hover', {
1711        detail: {
1712          data: rowObject.data,
1713          callBack: (): void => {
1714            this.clearAllHover(rowObject.data);
1715            this.setMouseIn(rowObject.data.isHover, elements);
1716          },
1717        },
1718        composed: true,
1719      })
1720    );
1721  }
1722
1723  setHighLight(isSearch: boolean, element: any): void {
1724    if (isSearch) {
1725      element.setAttribute('high-light', '');
1726    } else {
1727      element.removeAttribute('high-light');
1728    }
1729  }
1730
1731  createTextColor(rowData: any, divElement: any): void {
1732    let nodeText = document.createElement('text');
1733    nodeText.classList.add('functionName');
1734    nodeText.textContent = rowData.data.name;
1735    divElement.append(nodeText);
1736    if (rowData.data.scriptName !== 'unknown') {
1737      let scriptText = document.createElement('text');
1738      scriptText.classList.add('scriptName');
1739      scriptText.textContent = rowData.data.scriptName;
1740      divElement.append(scriptText);
1741      scriptText.style.color = '#a1a1a1';
1742    }
1743    divElement.title = rowData.data.symbolName;
1744  }
1745
1746  jsMemoryHandler(rowData: any, td: any) {
1747    if (rowData.data.rowName === 'js-memory') {
1748      let nodeText = document.createElement('text');
1749      nodeText.classList.add('nodeName');
1750      nodeText.textContent = rowData.data.nodeName;
1751      td.append(nodeText);
1752      let countText = document.createElement('text');
1753      countText.classList.add('countName');
1754      countText.textContent = rowData.data.count;
1755      td.append(countText);
1756      let nodeIdText = document.createElement('text');
1757      nodeIdText.classList.add('nodeIdText');
1758      nodeIdText.textContent = rowData.data.nodeId;
1759      td.append(nodeIdText);
1760      if (rowData.data.edgeName != '') {
1761        let edgeNameText = document.createElement('text');
1762        edgeNameText.classList.add('edgeNameText');
1763        edgeNameText.textContent = rowData.data.edgeName;
1764        td.insertBefore(edgeNameText, nodeText);
1765        let span = document.createElement('span');
1766        span.classList.add('span');
1767        if (rowData.data.type === ConstructorType.RetainersType) {
1768          span.textContent = '\xa0' + 'in' + '\xa0';
1769          nodeIdText.textContent = ` @${rowData.data.id}`;
1770        } else {
1771          span.textContent = '\xa0' + '::' + '\xa0';
1772        }
1773        edgeNameText.append(span);
1774      }
1775      if (
1776        (rowData.data.nodeType === NodeType.STRING ||
1777          rowData.data.nodeType === NodeType.CONCATENATED_STRING ||
1778          rowData.data.nodeType === NodeType.SLICED_STRING) &&
1779        rowData.data.type !== ConstructorType.ClassType
1780      ) {
1781        nodeText.style.color = '#d53d3d';
1782        nodeText.textContent = '"' + rowData.data.nodeName + '"';
1783      }
1784      td.title = rowData.data.objectName;
1785    }
1786  }
1787}
1788
1789// 表格默认是展开还是收起的
1790export enum TableMode {
1791  Expand, // 默认展开
1792  Retract, // 默认收起
1793}
1794
1795// 重绘的表格是要全部展开,全部收起,还是一层一层手动打开
1796export enum RedrawTreeForm {
1797  Expand, // 一键展开
1798  Retract, // 一键收起
1799  Default, //点击加号,逐层展开
1800}
1801