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, '<').replace(/>/g, '>'); 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