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 {element} from "../BaseElement.js"; 18import "../utils/Template.js" 19import {TableRowObject} from "./TableRowObject.js"; 20 21@element('lit-table') 22export class LitTable extends HTMLElement { 23 meauseRowElement: HTMLDivElement | undefined 24 currentRecycleList: HTMLDivElement[] = [] 25 private st: HTMLSlotElement | null | undefined 26 private tableElement: HTMLDivElement | null | undefined 27 private theadElement: HTMLDivElement | null | undefined 28 private columns: Array<Element> | null | undefined 29 private tbodyElement: HTMLDivElement | undefined | null 30 private tableColumns: NodeListOf<LitTableColumn> | undefined 31 private colCount: number = 0 32 private ds: Array<any> = [] 33 private recycleDs: Array<any> = [] 34 private gridTemplateColumns: any 35 36 constructor() { 37 super(); 38 const shadowRoot = this.attachShadow({mode: 'open'}); 39 shadowRoot.innerHTML = ` 40<style> 41:host{ 42 display: grid; 43 grid-template-columns: repeat(1,1fr); 44 width: 100%; 45 flex:1; 46} 47.tr{ 48 display: grid; 49 width:100%; 50} 51.tr:nth-of-type(even){ 52} 53.tr{ 54 background-color: var(--dark-background,#FFFFFF); 55} 56.tr:hover{ 57 background-color: var(--dark-background6,#DEEDFF); 58} 59.td{ 60 background-color: inherit; 61 box-sizing: border-box; 62 padding: 3px; 63 display: flex; 64 justify-content: flex-start; 65 align-items: center; 66 width: 100%; 67 height: auto; 68} 69.td-order{ 70} 71.td-order:before{ 72 73} 74:host([grid-line]) .td{ 75 border-left: 1px solid #f0f0f0; 76} 77:host([grid-line]) .td:last-of-type{ 78 border-right: 1px solid #f0f0f0; 79} 80.table{ 81 color: var(--dark-color2,#262626); 82} 83.thead{ 84 display: grid; 85 position: sticky; 86 top: 0; 87 font-weight: bold; 88 font-size: .9rem; 89 color: var(--dark-color1,#000); 90 z-index: 1; 91} 92.tbody{ 93 width: 100%; 94 top: 0; 95 left: 0; 96 right:0; 97 bottom:0; 98 display: grid; 99 grid-template-columns: 1fr; 100 row-gap: 1px; 101 column-gap: 1px; 102 position: relative; 103} 104:host([grid-line]) .tbody{ 105 border-bottom: 1px solid #f0f0f0; 106 background-color: #f0f0f0; 107} 108.th{ 109 display: grid; 110} 111 112.tree-icon{ 113 font-size: 1.2rem; 114 width: 20px; 115 height: 20px; 116 padding-right: 5px; 117 padding-left: 5px; 118 cursor: pointer; 119} 120.tree-icon:hover{ 121 color: #42b983; 122} 123.row-checkbox,row-checkbox-all{ 124 125} 126:host([no-head]) .thead{ 127 display: none; 128} 129.up-svg{ 130 position: absolute; 131 right: 5px; 132 top: 8px; 133 bottom: 8px; 134 width: 15px; 135 height: 15px; 136} 137.down-svg{ 138 position: absolute; 139 top: 8px; 140 right: 5px; 141 bottom: 8px; 142 width: 15px; 143 height: 15px; 144} 145</style> 146 147<slot id="slot" style="display: none"></slot> 148<div class="table" style="overflow:auto;"> 149 <div class="thead"></div> 150 <div class="tbody"></div> 151</div> 152 ` 153 } 154 155 static get observedAttributes() { 156 return ['scroll-y', 'selectable', 'no-head', 'grid-line', 'defaultOrderColumn'] 157 } 158 159 get selectable() { 160 return this.hasAttribute('selectable'); 161 } 162 163 set selectable(value) { 164 if (value) { 165 this.setAttribute('selectable', ''); 166 } else { 167 this.removeAttribute('selectable'); 168 } 169 } 170 171 get scrollY() { 172 return this.getAttribute('scroll-y') || 'auto'; 173 } 174 175 set scrollY(value) { 176 this.setAttribute('scroll-y', value); 177 } 178 179 get dataSource() { 180 return this.ds || []; 181 } 182 183 set dataSource(value) { 184 this.ds = value; 185 if (this.hasAttribute('tree')) { 186 this.renderTreeTable(); 187 } else { 188 this.renderTable(); 189 } 190 } 191 192 get recycleDataSource() { 193 return this.recycleDs || []; 194 } 195 196 set recycleDataSource(value) { 197 if (this.hasAttribute('tree')) { 198 this.recycleDs = this.meauseTreeRowElement(value) 199 } else { 200 this.recycleDs = this.meauseAllRowHeight(value) 201 } 202 } 203 204 connectedCallback() { 205 this.st = this.shadowRoot?.querySelector('#slot'); 206 this.tableElement = this.shadowRoot?.querySelector('.table'); 207 this.theadElement = this.shadowRoot?.querySelector('.thead'); 208 this.tbodyElement = this.shadowRoot?.querySelector('.tbody'); 209 this.tableColumns = this.querySelectorAll<LitTableColumn>('lit-table-column'); 210 this.colCount = this.tableColumns!.length; 211 this.st?.addEventListener('slotchange', () => { 212 this.theadElement!.innerHTML = ''; 213 setTimeout(() => { 214 this.columns = this.st!.assignedElements(); 215 let rowElement = document.createElement('div'); 216 rowElement.classList.add('th'); 217 if (this.selectable) { 218 let box = document.createElement('div'); 219 box.style.display = 'flex'; 220 box.style.justifyContent = 'center'; 221 box.style.alignItems = 'center'; 222 box.style.gridArea = "_checkbox_"; 223 box.classList.add('td'); 224 box.style.backgroundColor = "#ffffff66"; 225 let checkbox = document.createElement('lit-checkbox'); 226 checkbox.classList.add('row-checkbox-all'); 227 checkbox.onchange = (e: any) => { 228 this.shadowRoot!.querySelectorAll('.row-checkbox').forEach((a: any) => a.checked = e.detail.checked); 229 if (e.detail.checked) { 230 this.shadowRoot!.querySelectorAll('.tr').forEach(a => a.setAttribute('checked', '')); 231 } else { 232 this.shadowRoot!.querySelectorAll('.tr').forEach(a => a.removeAttribute('checked')); 233 } 234 } 235 236 box.appendChild(checkbox); 237 rowElement.appendChild(box); 238 } 239 240 let area: Array<any> = [], gridTemplateColumns: Array<any> = []; 241 let resolvingArea = (columns: any, x: any, y: any) => { 242 columns.forEach((a: any, i: any) => { 243 if (!area[y]) area[y] = [] 244 let key = a.getAttribute('key') || a.getAttribute('title') 245 if (a.tagName === 'LIT-TABLE-GROUP') { 246 let len = a.querySelectorAll('lit-table-column').length; 247 let children = [...a.children].filter(a => a.tagName !== 'TEMPLATE'); 248 if (children.length > 0) { 249 resolvingArea(children, x, y + 1); 250 } 251 for (let j = 0; j < len; j++) { 252 area[y][x] = {x, y, t: key}; 253 x++; 254 } 255 let h = document.createElement('div'); 256 h.classList.add('td'); 257 h.style.justifyContent = a.getAttribute('align') 258 h.style.borderBottom = '1px solid #f0f0f0' 259 h.style.gridArea = key; 260 h.innerText = a.title; 261 if (a.hasAttribute('fixed')) { 262 this.fixed(h, a.getAttribute('fixed'), "#42b983") 263 } 264 rowElement.append(h); 265 } else if (a.tagName === 'LIT-TABLE-COLUMN') { 266 area[y][x] = {x, y, t: key}; 267 x++; 268 let h: any = document.createElement('div'); 269 h.classList.add('td'); 270 if (a.hasAttribute('order')) { 271 h.sortType = 0; 272 h.classList.add('td-order'); 273 h.style.position = "relative" 274 let NS = "http://www.w3.org/2000/svg"; 275 let upSvg: any = document.createElementNS(NS, "svg"); 276 let upPath: any = document.createElementNS(NS, "path"); 277 upSvg.setAttribute('fill', '#efefef'); 278 upSvg.setAttribute('viewBox', '0 0 1024 1024'); 279 upSvg.setAttribute('stroke', '#000000'); 280 upSvg.classList.add('up-svg'); 281 upPath.setAttribute("d", "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"); 282 upSvg.appendChild(upPath); 283 let downSvg: any = document.createElementNS(NS, "svg"); 284 let downPath: any = document.createElementNS(NS, "path"); 285 downSvg.setAttribute('fill', '#efefef'); 286 downSvg.setAttribute('viewBox', '0 0 1024 1024'); 287 downSvg.setAttribute('stroke', '#efefef'); 288 downSvg.classList.add('down-svg'); 289 downPath.setAttribute("d", "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"); 290 downSvg.appendChild(downPath) 291 if (i == 0) { 292 h.sortType = 0; 293 upSvg.setAttribute('fill', '#fff'); 294 downSvg.setAttribute('fill', '#fff'); 295 } 296 upSvg.style.display = 'none'; 297 downSvg.style.display = 'none'; 298 h.appendChild(upSvg); 299 h.appendChild(downSvg); 300 h.onclick = () => { 301 this?.shadowRoot?.querySelectorAll('.td-order svg').forEach((it: any) => { 302 it.setAttribute('fill', '#fff'); 303 it.setAttribute('fill', '#fff'); 304 it.sortType = 0; 305 }) 306 if (h.sortType == undefined || h.sortType == null) { 307 h.sortType = 0; 308 } else if (h.sortType === 2) { 309 h.sortType = 0; 310 } else { 311 h.sortType += 1; 312 } 313 switch (h.sortType) { 314 case 1: 315 upSvg.setAttribute('fill', '#333'); 316 downSvg.setAttribute('fill', '#fff'); 317 upSvg.style.display = 'block'; 318 downSvg.style.display = 'none'; 319 break; 320 case 2: 321 upSvg.setAttribute('fill', '#fff'); 322 downSvg.setAttribute('fill', '#333'); 323 upSvg.style.display = 'none'; 324 downSvg.style.display = 'block'; 325 break; 326 default: 327 upSvg.setAttribute('fill', "#fff"); 328 downSvg.setAttribute('fill', "#fff"); 329 upSvg.style.display = 'none'; 330 downSvg.style.display = 'none'; 331 break; 332 } 333 this.dispatchEvent(new CustomEvent("column-click", { 334 detail: { 335 sort: h.sortType, key: key 336 }, composed: true 337 })) 338 } 339 } 340 h.style.justifyContent = a.getAttribute('align') 341 gridTemplateColumns.push(a.getAttribute('width') || '1fr'); 342 h.style.gridArea = key; 343 let titleLabel = document.createElement("label"); 344 titleLabel.textContent = a.title; 345 h.appendChild(titleLabel); 346 if (a.hasAttribute('fixed')) { 347 this.fixed(h, a.getAttribute('fixed'), "#42b983") 348 } 349 rowElement.append(h); 350 } 351 }) 352 } 353 resolvingArea(this.columns, 0, 0); 354 area.forEach((rows, j, array) => { 355 for (let i = 0; i < this.colCount; i++) { 356 if (!rows[i]) rows[i] = array[j - 1][i]; 357 } 358 }) 359 this.gridTemplateColumns = gridTemplateColumns.join(' '); 360 if (this.selectable) { 361 let s = area.map(a => '"_checkbox_ ' + (a.map((aa: any) => aa.t).join(' ')) + '"').join(' '); 362 rowElement.style.gridTemplateColumns = "60px " + gridTemplateColumns.join(' ');//`repeat(${this.colCount},1fr)` 363 rowElement.style.gridTemplateRows = `repeat(${area.length},1fr)` 364 rowElement.style.gridTemplateAreas = s 365 } else { 366 let s = area.map(a => '"' + (a.map((aa: any) => aa.t).join(' ')) + '"').join(' '); 367 rowElement.style.gridTemplateColumns = gridTemplateColumns.join(' ');//`repeat(${this.colCount},1fr)` 368 rowElement.style.gridTemplateRows = `repeat(${area.length},1fr)` 369 rowElement.style.gridTemplateAreas = s 370 } 371 this.theadElement!.append(rowElement); 372 if (this.hasAttribute('tree')) { 373 this.renderTreeTable(); 374 } else { 375 this.renderTable(); 376 } 377 }); 378 379 }); 380 381 this.shadowRoot!.addEventListener("load", function (event) { 382 console.log("DOM fully loaded and parsed"); 383 }); 384 } 385 386 disconnectedCallback() { 387 } 388 389 adoptedCallback() { 390 console.log('Custom square element moved to new page.'); 391 } 392 393 attributeChangedCallback(name: string, oldValue: string, newValue: string) { 394 } 395 396 fixed(td: HTMLElement, placement: string, bgColor: string) { 397 td.style.position = 'sticky'; 398 if (placement === "left") { 399 td.style.left = '0px'; 400 td.style.boxShadow = '3px 0px 5px #33333333' 401 } else if (placement === "right") { 402 td.style.right = '0px'; 403 td.style.boxShadow = '-3px 0px 5px #33333333' 404 } 405 } 406 407 renderTable() { 408 if (!this.columns) return; 409 if (!this.ds) return; 410 this.tbodyElement!.innerHTML = ''; 411 this.ds.forEach((rowData: any) => { 412 let rowElement = document.createElement('div'); 413 rowElement.classList.add('tr'); 414 // @ts-ignore 415 rowElement.data = rowData; 416 let gridTemplateColumns: Array<any> = [] 417 if (this.selectable) { 418 let box = document.createElement('div'); 419 box.style.display = 'flex'; 420 box.style.justifyContent = 'center'; 421 box.style.alignItems = 'center'; 422 box.classList.add('td'); 423 let checkbox = document.createElement('lit-checkbox'); 424 checkbox.classList.add('row-checkbox'); 425 checkbox.onchange = (e: any) => { 426 if (e.detail.checked) { 427 rowElement.setAttribute('checked', ""); 428 } else { 429 rowElement.removeAttribute('checked'); 430 } 431 } 432 box.appendChild(checkbox); 433 rowElement.appendChild(box); 434 } 435 this.tableColumns!.forEach(cl => { 436 let dataIndex = cl.getAttribute('data-index') || '1'; 437 gridTemplateColumns.push(cl.getAttribute('width') || '1fr') 438 if (cl.template) { 439 // @ts-ignore 440 let cloneNode = cl.template.render(rowData).content.cloneNode(true); 441 let d = document.createElement('div'); 442 d.classList.add('td'); 443 d.style.wordBreak = 'break-all' 444 d.style.whiteSpace = 'pre-wrap' 445 d.style.justifyContent = cl.getAttribute('align') || '' 446 if (cl.hasAttribute('fixed')) { 447 this.fixed(d, cl.getAttribute('fixed') || '', "#ffffff") 448 } 449 d.append(cloneNode); 450 rowElement.append(d); 451 } else { 452 let td = document.createElement('div'); 453 td.classList.add('td'); 454 td.style.wordBreak = 'break-all' 455 td.style.whiteSpace = 'pre-wrap' 456 td.title = rowData[dataIndex] 457 td.style.justifyContent = cl.getAttribute('align') || '' 458 if (cl.hasAttribute('fixed')) { 459 this.fixed(td, cl.getAttribute('fixed') || '', "#ffffff") 460 } 461 td.innerHTML = rowData[dataIndex]; 462 rowElement.append(td); 463 } 464 465 }) 466 if (this.selectable) { 467 rowElement.style.gridTemplateColumns = '60px ' + gridTemplateColumns.join(' '); 468 } else { 469 rowElement.style.gridTemplateColumns = gridTemplateColumns.join(' '); 470 } 471 rowElement.onclick = e => { 472 this.dispatchEvent(new CustomEvent('row-click', {detail: rowData, composed: true})); 473 } 474 this.tbodyElement!.append(rowElement); 475 }) 476 } 477 478 renderTreeTable() { 479 if (!this.columns) return; 480 if (!this.ds) return; 481 this.tbodyElement!.innerHTML = ''; 482 let ids = JSON.parse(this.getAttribute('tree') || `["id","pid"]`); 483 let toTreeData = (data: any, id: any, pid: any) => { 484 let cloneData = JSON.parse(JSON.stringify(data)); 485 return cloneData.filter((father: any) => { 486 let branchArr = cloneData.filter((child: any) => father[id] == child[pid]); 487 branchArr.length > 0 ? father['children'] = branchArr : ''; 488 return !father[pid]; 489 }); 490 } 491 let treeData = toTreeData(this.ds, ids[0], ids[1]);// 492 let offset = 30; 493 let offsetVal = offset; 494 const drawRow = (arr: any, parentNode: any) => { 495 arr.forEach((rowData: any) => { 496 let rowElement = document.createElement('div'); 497 rowElement.classList.add('tr'); 498 // @ts-ignore 499 rowElement.data = rowData; 500 let gridTemplateColumns: Array<any> = []; 501 if (this.selectable) { 502 let box = document.createElement('div'); 503 box.style.display = 'flex'; 504 box.style.justifyContent = 'center'; 505 box.style.alignItems = 'center'; 506 box.classList.add('td'); 507 let checkbox = document.createElement('lit-checkbox'); 508 checkbox.classList.add('row-checkbox'); 509 checkbox.onchange = (e: any) => { 510 if (e.detail.checked) { 511 rowElement.setAttribute('checked', ""); 512 } else { 513 rowElement.removeAttribute('checked'); 514 } 515 const changeChildNode = (rowElement: any, checked: any) => { 516 let id = rowElement.getAttribute('id'); 517 let pid = rowElement.getAttribute('pid'); 518 this.shadowRoot!.querySelectorAll(`div[pid=${id}]`).forEach(a => { 519 // @ts-ignore 520 a.querySelector('.row-checkbox')!.checked = checked; 521 if (checked) { 522 a.setAttribute('checked', ''); 523 } else { 524 a.removeAttribute('checked'); 525 } 526 changeChildNode(a, checked); 527 }); 528 }; 529 changeChildNode(rowElement, e.detail.checked); 530 } 531 box.appendChild(checkbox); 532 rowElement.appendChild(box); 533 } 534 this.tableColumns!.forEach((cl, index) => { 535 let dataIndex = cl.getAttribute('data-index'); 536 gridTemplateColumns.push(cl.getAttribute('width') || '1fr') 537 let td; 538 if (cl.template) { 539 // @ts-ignore 540 let cloneNode = cl.template.render(rowData).content.cloneNode(true); 541 td = document.createElement('div'); 542 td.classList.add('td'); 543 td.style.wordBreak = 'break-all' 544 td.style.justifyContent = cl.getAttribute('align') || '' 545 if (cl.hasAttribute('fixed')) { 546 this.fixed(td, cl.getAttribute('fixed') || '', "#ffffff") 547 } 548 td.append(cloneNode); 549 } else { 550 td = document.createElement('div'); 551 td.classList.add('td'); 552 td.style.wordBreak = 'break-all' 553 td.style.justifyContent = cl.getAttribute('align') || '' 554 if (cl.hasAttribute('fixed')) { 555 this.fixed(td, cl.getAttribute('fixed') || '', "#ffffff") 556 } 557 // @ts-ignore 558 td.innerHTML = rowData[dataIndex]; 559 } 560 if (index === 0) { 561 if (rowData.children && rowData.children.length > 0) { 562 let btn = document.createElement('lit-icon'); 563 btn.classList.add('tree-icon'); 564 // @ts-ignore 565 btn.name = 'minus-square'; 566 td.insertBefore(btn, td.firstChild); 567 td.style.paddingLeft = (offsetVal - 30) + 'px'; 568 btn.onclick = (e) => { 569 const foldNode = (rowElement: any) => { 570 let id = rowElement.getAttribute('id'); 571 let pid = rowElement.getAttribute('pid'); 572 this.shadowRoot!.querySelectorAll(`div[pid=${id}]`).forEach(a => { 573 let id = a.getAttribute('id'); 574 let pid = a.getAttribute('pid'); 575 (a as HTMLElement).style.display = 'none'; 576 foldNode(a); 577 }); 578 if (rowElement.querySelector('.tree-icon')) { 579 rowElement.querySelector('.tree-icon').name = 'plus-square'; 580 } 581 rowElement.removeAttribute('expend'); 582 }; 583 const expendNode = (rowElement: any) => { 584 let id = rowElement.getAttribute('id'); 585 let pid = rowElement.getAttribute('pid'); 586 this.shadowRoot!.querySelectorAll(`div[pid=${id}]`).forEach((a) => { 587 let id = a.getAttribute('id'); 588 let pid = a.getAttribute('pid'); 589 (a as HTMLElement).style.display = ''; 590 }); 591 if (rowElement.querySelector('.tree-icon')) { 592 rowElement.querySelector('.tree-icon').name = 'minus-square'; 593 } 594 rowElement.setAttribute('expend', ''); 595 } 596 if (rowElement.hasAttribute('expend')) { 597 foldNode(rowElement); 598 } else { 599 expendNode(rowElement); 600 } 601 e.stopPropagation(); 602 }; 603 } else { 604 td.style.paddingLeft = offsetVal + 'px'; 605 } 606 } 607 rowElement.append(td); 608 }) 609 if (this.selectable) { 610 rowElement.style.gridTemplateColumns = '60px ' + gridTemplateColumns.join(' '); 611 } else { 612 rowElement.style.gridTemplateColumns = gridTemplateColumns.join(' '); 613 } 614 rowElement.onclick = e => { 615 this.dispatchEvent(new CustomEvent('row-click', {detail: rowData, composed: true})); 616 } 617 rowElement.style.cursor = 'pointer' 618 parentNode.append(rowElement); 619 rowElement.setAttribute('id', rowData[ids[0]]); 620 rowElement.setAttribute('pid', rowData[ids[1]]); 621 rowElement.setAttribute('expend', ''); 622 if (rowData.children && rowData.children.length > 0) { 623 offsetVal = offsetVal + offset; 624 drawRow(rowData.children, parentNode); 625 offsetVal = offsetVal - offset; 626 } 627 }); 628 }; 629 drawRow(treeData, this.tbodyElement); 630 } 631 632 getCheckRows() { 633 // @ts-ignore 634 return [...this.shadowRoot!.querySelectorAll('div[class=tr][checked]')].map(a => a.data).map(a => { 635 delete a['children']; 636 return a; 637 }); 638 } 639 640 deleteRowsCondition(fn: any) { 641 this.shadowRoot!.querySelectorAll("div[class=tr]").forEach(tr => { 642 // @ts-ignore 643 if (fn(tr.data)) { 644 tr.remove(); 645 } 646 }) 647 } 648 649 meauseElementHeight(rowData: any) { 650 if (this.meauseRowElement == undefined) { 651 this.meauseRowElement = this.createNewTableElement(rowData) 652 this.meauseRowElement!.style.width = this.tableElement!.clientWidth + "px" 653 this.meauseRowElement!.style.top = "-100px" 654 this.meauseRowElement!.style.position = 'absolute' 655 this.meauseRowElement!.style.left = "0px" 656 this.tbodyElement?.append(this.meauseRowElement!) 657 } else { 658 this.meauseRowElement.childNodes.forEach((children: any, index: number) => { 659 if (children.template) { 660 children.innerHTML = children.template.render(rowData).content.cloneNode(true).innerHTML 661 } else { 662 children.innerHTML = rowData[children.dataIndex] 663 } 664 }) 665 } 666 return this.meauseRowElement!.clientHeight 667 } 668 669 meauseTreeElementHeight(rowData: any, depth: number) { 670 if (this.meauseRowElement == undefined) { 671 this.meauseRowElement = this.createNewTreeTableElement(rowData) 672 this.meauseRowElement!.style.width = this.tableElement!.clientWidth + "px" 673 this.meauseRowElement!.style.top = "-100px" 674 this.meauseRowElement!.style.position = 'absolute' 675 this.meauseRowElement!.style.left = "0px" 676 this.tbodyElement?.append(this.meauseRowElement!) 677 } else { 678 this.meauseRowElement.childNodes.forEach((children: any, index: number) => { 679 if (index == 0) { 680 children.style.paddingLeft = depth * 30 + "px" 681 } 682 if (children.template) { 683 children.innerHTML = children.template.render(rowData.data).content.cloneNode(true).innerHTML 684 } else { 685 children.innerHTML = rowData.data[children.dataIndex] 686 } 687 }) 688 } 689 return this.meauseRowElement!.clientHeight 690 } 691 692 meauseAllRowHeight(list: any[]): TableRowObject[] { 693 this.tbodyElement!.innerHTML = ''; 694 this.meauseRowElement = undefined 695 this.tbodyElement && (this.tbodyElement.style.width = this.tableElement?.clientWidth + "px") 696 this.currentRecycleList = [] 697 let headHeight = this.theadElement?.clientHeight || 0 698 let totalHeight = headHeight 699 let visibleObjects: TableRowObject[] = []; 700 list.forEach((rowData, index) => { 701 let height = this.meauseElementHeight(rowData); 702 let tableRowObject = new TableRowObject(); 703 tableRowObject.height = height 704 tableRowObject.top = totalHeight 705 tableRowObject.data = rowData 706 tableRowObject.rowIndex = index 707 if (Math.max(totalHeight, this.tableElement!.scrollTop + headHeight) <= Math.min(totalHeight + height, this.tableElement!.scrollTop + this.tableElement!.clientHeight + headHeight)) { 708 let newTableElement = this.createNewTableElement(tableRowObject); 709 newTableElement.style.transform = `translateY(${totalHeight}px)` 710 this.tbodyElement?.append(newTableElement) 711 this.currentRecycleList.push(newTableElement) 712 } 713 totalHeight += height 714 visibleObjects.push(tableRowObject) 715 }) 716 this.tbodyElement && (this.tbodyElement.style.height = totalHeight + "px") 717 this.tableElement && (this.tableElement.onscroll = (event) => { 718 let top = this.tableElement!.scrollTop + headHeight; 719 let skip = 0; 720 for (let i = 0; i < visibleObjects.length; i++) { 721 if (visibleObjects[i].top <= top && visibleObjects[i].top + visibleObjects[i].height >= top) { 722 skip = i 723 break; 724 } 725 } 726 let reduce = this.currentRecycleList.map((item) => item.clientHeight).reduce((a, b) => a + b); 727 while (reduce <= this.tableElement!.clientHeight) { 728 let newTableElement = this.createNewTableElement(visibleObjects[skip].data); 729 this.tbodyElement?.append(newTableElement) 730 this.currentRecycleList.push(newTableElement) 731 reduce += newTableElement.clientHeight 732 } 733 for (let i = 0; i < this.currentRecycleList.length; i++) { 734 this.freshCurrentLine(this.currentRecycleList[i], visibleObjects[i + skip]) 735 } 736 }) 737 return visibleObjects 738 } 739 740 meauseTreeRowElement(list: any[]): TableRowObject[] { 741 this.meauseRowElement = undefined 742 this.tbodyElement!.innerHTML = ''; 743 this.tbodyElement && (this.tbodyElement.style.width = this.tableElement?.clientWidth + "px") 744 let headHeight = this.theadElement?.clientHeight || 0 745 let totalHeight = headHeight 746 let visibleObjects: TableRowObject[] = [] 747 this.currentRecycleList = [] 748 let resetAllHeight = (list: any[], depth: number, parentNode?: TableRowObject) => { 749 list.forEach((item) => { 750 let tableRowObject = new TableRowObject(); 751 tableRowObject.depth = depth 752 tableRowObject.data = item 753 tableRowObject.top = totalHeight//初始化高度 754 tableRowObject.height = this.meauseTreeElementHeight(tableRowObject, depth) 755 if (parentNode != undefined) { 756 parentNode.children.push(tableRowObject) 757 } 758 if (Math.max(totalHeight, this.tableElement!.scrollTop + headHeight) <= Math.min(totalHeight + tableRowObject.height, this.tableElement!.scrollTop + this.tableElement!.clientHeight + headHeight)) { 759 let newTableElement = this.createNewTreeTableElement(tableRowObject); 760 newTableElement.style.transform = `translateY(${totalHeight}px)` 761 this.tbodyElement?.append(newTableElement) 762 this.currentRecycleList.push(newTableElement) 763 } 764 totalHeight += tableRowObject.height 765 visibleObjects.push(tableRowObject) 766 if (item.children != undefined && item.children.length > 0) { 767 resetAllHeight(item.children, depth + 1, tableRowObject) 768 } 769 }) 770 } 771 resetAllHeight(list, 0) 772 console.log(visibleObjects); 773 this.tbodyElement && (this.tbodyElement.style.height = totalHeight + "px") 774 this.tableElement && (this.tableElement.onscroll = (event) => { 775 let visibleObjects = this.recycleDs.filter((item) => { 776 return !item.rowHidden 777 }) 778 let top = this.tableElement!.scrollTop + headHeight; 779 let skip = 0; 780 for (let i = 0; i < visibleObjects.length; i++) { 781 if (visibleObjects[i].top <= top && visibleObjects[i].top + visibleObjects[i].height >= top) { 782 skip = i 783 break; 784 } 785 } 786 let reduce = this.currentRecycleList.map((item) => item.clientHeight).reduce((a, b) => a + b); 787 while (reduce <= this.tableElement!.clientHeight) { 788 let newTableElement = this.createNewTreeTableElement(visibleObjects[skip]); 789 this.tbodyElement?.append(newTableElement) 790 this.currentRecycleList.push(newTableElement) 791 reduce += newTableElement.clientHeight 792 } 793 for (let i = 0; i < this.currentRecycleList.length; i++) { 794 console.log(visibleObjects[i + skip]); 795 this.freshCurrentLine(this.currentRecycleList[i], visibleObjects[i + skip]) 796 } 797 }) 798 return visibleObjects 799 } 800 801 802 createNewTreeTableElement(rowData: TableRowObject): any { 803 let newTableElement = document.createElement('div'); 804 newTableElement.classList.add('tr'); 805 let gridTemplateColumns: Array<any> = []; 806 this?.columns?.forEach((column: any, index) => { 807 let dataIndex = column.getAttribute('data-index') || '1'; 808 gridTemplateColumns.push(column.getAttribute('width') || '1fr') 809 let td: any 810 if (column.template) { 811 td = column.template.render(rowData.data).content.cloneNode(true); 812 td.template = column.template 813 } else { 814 td = document.createElement('div') 815 td.classList.add('td'); 816 td.style.wordBreak = 'break-all' 817 td.innerHTML = rowData.data[dataIndex]; 818 td.dataIndex = dataIndex 819 } 820 if (index === 0) { 821 if (rowData.data.children && rowData.data.children.length > 0) { 822 let btn = this.createExpandBtn(rowData) 823 td.insertBefore(btn, td.firstChild); 824 td.style.paddingLeft = rowData.depth * 30 + 'px'; 825 } else { 826 td.style.paddingLeft = rowData.depth * 30 + 'px'; 827 } 828 } 829 newTableElement.append(td) 830 }) 831 newTableElement.style.gridTemplateColumns = gridTemplateColumns.join(' '); 832 newTableElement.style.position = 'absolute'; 833 newTableElement.style.top = '0px' 834 newTableElement.style.left = '0px' 835 return newTableElement 836 } 837 838 createExpandBtn(rowData: any) { 839 let btn: any = document.createElement('lit-icon'); 840 btn.classList.add('tree-icon'); 841 // @ts-ignore 842 if (rowData.expanded) { 843 btn.name = 'minus-square'; 844 } else { 845 btn.name = 'plus-square'; 846 } 847 btn.onclick = (e: Event) => { 848 const resetNodeHidden = (hidden: boolean, rowData: any) => { 849 if (rowData.children.length > 0) { 850 if (hidden) { 851 rowData.children.forEach((child: any) => { 852 child.rowHidden = true 853 resetNodeHidden(hidden, child) 854 }) 855 } else { 856 rowData.children.forEach((child: any) => { 857 child.rowHidden = !rowData.expanded 858 if (rowData.expanded) { 859 resetNodeHidden(hidden, child) 860 } 861 }) 862 } 863 } 864 } 865 const foldNode = () => { 866 rowData.expanded = false 867 resetNodeHidden(true, rowData) 868 }; 869 const expendNode = () => { 870 rowData.expanded = true 871 resetNodeHidden(false, rowData) 872 } 873 if (rowData.expanded) { 874 foldNode() 875 } else { 876 expendNode() 877 } 878 this.reMeauseHeight() 879 e.stopPropagation(); 880 }; 881 return btn 882 } 883 884 reMeauseHeight() { 885 let headHeight = this.theadElement?.clientHeight || 0 886 let totalHeight = headHeight 887 this.recycleDs.forEach((it) => { 888 if (!it.rowHidden) { 889 it.top = totalHeight 890 totalHeight += it.height 891 } 892 }) 893 this.tbodyElement && (this.tbodyElement.style.height = totalHeight + "px") 894 let visibleObjects = this.recycleDs.filter((item) => { 895 return !item.rowHidden 896 }) 897 let top = this.tableElement!.scrollTop + headHeight; 898 let skip = 0; 899 for (let i = 0; i < visibleObjects.length; i++) { 900 if (visibleObjects[i].top <= top && visibleObjects[i].top + visibleObjects[i].height >= top) { 901 skip = i 902 break; 903 } 904 } 905 let reduce = this.currentRecycleList.map((item) => item.clientHeight).reduce((a, b) => a + b); 906 while (reduce <= this.tableElement!.clientHeight) { 907 let newTableElement = this.createNewTreeTableElement(visibleObjects[skip]); 908 this.tbodyElement?.append(newTableElement) 909 this.currentRecycleList.push(newTableElement) 910 reduce += newTableElement.clientHeight 911 } 912 for (let i = 0; i < this.currentRecycleList.length; i++) { 913 this.freshCurrentLine(this.currentRecycleList[i], visibleObjects[i + skip]) 914 } 915 } 916 917 createNewTableElement(rowData: any): any { 918 let newTableElement = document.createElement('div'); 919 newTableElement.classList.add('tr'); 920 let gridTemplateColumns: Array<any> = []; 921 this?.columns?.forEach((column: any) => { 922 let dataIndex = column.getAttribute('data-index') || '1'; 923 gridTemplateColumns.push(column.getAttribute('width') || '1fr') 924 let td: any 925 if (column.template) { 926 td = column.template.render(rowData).content.cloneNode(true); 927 td.template = column.template 928 } else { 929 td = document.createElement('div') 930 td.classList.add('td'); 931 td.style.wordBreak = 'break-all' 932 td.innerHTML = rowData[dataIndex]; 933 td.dataIndex = dataIndex 934 } 935 newTableElement.append(td) 936 }) 937 newTableElement.style.gridTemplateColumns = gridTemplateColumns.join(' '); 938 newTableElement.style.position = 'absolute'; 939 newTableElement.style.top = '0px' 940 newTableElement.style.left = '0px' 941 return newTableElement 942 } 943 944 freshCurrentLine(element: HTMLElement, rowObject: TableRowObject) { 945 element.childNodes.forEach((child, index) => { 946 if ((this.columns![index] as any).template) { 947 (child as HTMLElement).innerHTML = (this.columns![index] as any).template.render(rowObject.data).content.cloneNode(true).innerHTML 948 } else { 949 let dataIndex = this.columns![index].getAttribute('data-index') || '1'; 950 (child as HTMLElement).innerHTML = rowObject.data[dataIndex] 951 } 952 if (rowObject.depth != -1 && index == 0) { 953 if (rowObject.children && rowObject.children.length > 0) { 954 let btn = this.createExpandBtn(rowObject) 955 child.insertBefore(btn, child.firstChild); 956 } 957 (child as HTMLElement).style.paddingLeft = 30 * rowObject.depth + "px" 958 } 959 }) 960 element.style.transform = `translateY(${rowObject.top}px)` 961 } 962} 963