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