• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import {LitTableColumn} from "./lit-table-column.js";
17import {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