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