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 {BaseElement, element} from "../../../../base-ui/BaseElement.js"; 17import {TraceRowObject} from "./TraceRowObject.js"; 18import {TraceRow} from "./TraceRow.js"; 19 20@element("trace-row-recycler-view") 21export class TraceRowRecyclerView extends BaseElement { 22 private recycler: boolean = true; 23 private gasketEL: HTMLDivElement | null | undefined; 24 private container: HTMLDivElement | null | undefined; 25 private visibleRowsCount: number = 0; 26 private visibleObjects: TraceRowObject<any>[] = []; 27 private totalHeight: number = 0; 28 29 private _dataSource: Array<TraceRowObject<any>> = []; 30 31 get dataSource(): Array<TraceRowObject<any>> { 32 return this._dataSource; 33 } 34 35 set dataSource(value: Array<TraceRowObject<any>>) { 36 this._dataSource = value; 37 this.measureHeight(); 38 this.initUI(); 39 let els = [...(this.shadowRoot!.querySelectorAll<TraceRow<any>>(".recycler-cell"))]; 40 for (let i = 0; i < els.length; i++) { 41 this.refreshRow(els[i], this.visibleObjects[i]); 42 } 43 } 44 45 private _renderType: string = 'div'; 46 47 get renderType(): string { 48 return this._renderType; 49 } 50 51 set renderType(value: string) { 52 this._renderType = value; 53 } 54 55 refreshRow(el: TraceRow<any>, obj: TraceRowObject<any>) { 56 if (!obj) { 57 return; 58 } 59 el.obj = obj; 60 el.folder = obj.folder; 61 el.style.top = `${obj.top}px`; 62 el.name = obj.name || ''; 63 if (obj.children) { 64 el.setAttribute("children", ``); 65 } else { 66 el.removeAttribute("children"); 67 } 68 el.style.visibility = 'visible'; 69 el.rowId = obj.rowId 70 el.rowType = obj.rowType 71 el.rowParentId = obj.rowParentId 72 el.expansion = obj.expansion 73 el.rowHidden = obj.rowHidden 74 el.setAttribute("height", `${obj.rowHeight}`); 75 requestAnimationFrame(() => { 76 el.drawObject(); 77 }) 78 } 79 80 initElements(): void { 81 this.container = this.shadowRoot?.querySelector<HTMLDivElement>(".container"); 82 this.gasketEL = this.shadowRoot?.querySelector<HTMLDivElement>(".gasket"); 83 let els: Array<TraceRow<any>> | undefined | null; 84 this.container!.onscroll = (ev) => { 85 let top = this.container!.scrollTop; 86 let skip = 0; 87 for (let i = 0; i < this.visibleObjects.length; i++) { 88 if (this.visibleObjects[i].top >= top) { 89 skip = this.visibleObjects[i].rowIndex - 1; 90 break; 91 } 92 } 93 if (skip < 0) skip = 0; 94 if (!els) els = [...(this.shadowRoot!.querySelectorAll<TraceRow<any>>(".recycler-cell"))]; 95 for (let i = 0; i < els.length; i++) { 96 let obj = this.visibleObjects[i + skip]; 97 this.refreshRow(els[i], obj); 98 } 99 } 100 } 101 102 measureHeight() { 103 this.visibleObjects = this.dataSource.filter(it => !it.rowHidden); 104 this.totalHeight = this.visibleObjects.map((it) => it.rowHeight).reduce((a, b) => a + b); 105 let totalHeight = 0; 106 for (let i = 0; i < this.visibleObjects.length; i++) { 107 this.visibleObjects[i].top = totalHeight; 108 this.visibleObjects[i].rowIndex = i; 109 totalHeight += this.visibleObjects[i].rowHeight; 110 this.visibleObjects[i].preObject = i == 0 ? null : this.visibleObjects[i - 1]; 111 this.visibleObjects[i].nextObject = i == this.visibleObjects.length - 1 ? null : this.visibleObjects[i + 1]; 112 } 113 this.gasketEL && (this.gasketEL.style.height = `${this.totalHeight}px`); 114 } 115 116 initUI() { 117 this.visibleRowsCount = Math.ceil(this.clientHeight / 40); 118 if (this.visibleRowsCount >= this.visibleObjects.length) { 119 this.visibleRowsCount = this.visibleObjects.length; 120 } 121 if (!this.recycler) this.visibleRowsCount = this.dataSource.length; 122 for (let i = 0; i <= this.visibleRowsCount; i++) { 123 let el = new TraceRow<any>({alpha: true, contextId: '2d', isOffScreen: true}); 124 el.className = "recycler-cell" 125 this.container?.appendChild(el); 126 el.addEventListener('expansion-change', (ev: any) => { 127 el.obj!.expansion = ev.detail.expansion; 128 console.log(ev.detail, el.obj); 129 for (let j = 0; j < this.dataSource.length; j++) { 130 if (this.dataSource[j].rowParentId == ev.detail.rowId) { 131 this.dataSource[j].rowHidden = !ev.detail.expansion; 132 } 133 } 134 this.measureHeight(); 135 let els = [...(this.shadowRoot!.querySelectorAll<TraceRow<any>>(".recycler-cell"))]; 136 let top = this.container!.scrollTop; 137 let skip = 0; 138 for (let i = 0; i < this.visibleObjects.length; i++) { 139 if (this.visibleObjects[i].top >= top) { 140 skip = this.visibleObjects[i].rowIndex - 1; 141 break; 142 } 143 } 144 if (skip < 0) skip = 0; 145 for (let i = 0; i < els.length; i++) { 146 let obj = this.visibleObjects[i + skip]; 147 this.refreshRow(els[i], obj); 148 } 149 }) 150 } 151 } 152 153 initHtml(): string { 154 return ` 155<style> 156:host{ 157 width:100%; 158 height:100%; 159 display: block; 160 position:relative; 161} 162.container{ 163 width:100%; 164 height:100%; 165 overflow: auto; 166 position: absolute; 167 display: block; 168} 169.gasket{ 170 width:100%; 171 height:auto; 172 top: 0; 173 left: 0; 174 right:0; 175 bottom:0; 176 visibility: hidden; 177} 178.recycler-cell{ 179 position: absolute; 180 width:100%; 181 visibility: hidden; 182 top: 0; 183 left: 0; 184} 185</style> 186<div class="container"> 187 <div class="gasket"></div> 188</div> 189 190`; 191 } 192 193} 194