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 */ 15import {BaseElement, element} from "../../../../base-ui/BaseElement.js"; 16import {TraceRowObject} from "./TraceRowObject.js"; 17import {TraceRow} from "./TraceRow.js"; 18import {log} from "../../../../log/Log.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 private _renderType: string = 'div'; 31 32 get dataSource(): Array<TraceRowObject<any>> { 33 return this._dataSource; 34 } 35 36 set dataSource(value: Array<TraceRowObject<any>>) { 37 log("dataSource TraceRowObject size :" + value.length); 38 this._dataSource = value; 39 this.measureHeight(); 40 this.initUI(); 41 let els = [...(this.shadowRoot!.querySelectorAll<TraceRow<any>>(".recycler-cell"))]; 42 for (let i = 0; i < els.length; i++) { 43 this.refreshRow(els[i], this.visibleObjects[i]); 44 } 45 } 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 }) 77 } 78 79 initElements(): void { 80 this.container = this.shadowRoot?.querySelector<HTMLDivElement>(".container"); 81 this.gasketEL = this.shadowRoot?.querySelector<HTMLDivElement>(".gasket"); 82 let els: Array<TraceRow<any>> | undefined | null; 83 this.container!.onscroll = (ev) => { 84 let top = this.container!.scrollTop; 85 let skip = 0; 86 for (let i = 0; i < this.visibleObjects.length; i++) { 87 if (this.visibleObjects[i].top >= top) { 88 skip = this.visibleObjects[i].rowIndex - 1; 89 break; 90 } 91 } 92 if (skip < 0) skip = 0; 93 if (!els) els = [...(this.shadowRoot!.querySelectorAll<TraceRow<any>>(".recycler-cell"))]; 94 for (let i = 0; i < els.length; i++) { 95 let obj = this.visibleObjects[i + skip]; 96 this.refreshRow(els[i], obj); 97 } 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>({canvasNumber: 1, 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 for (let j = 0; j < this.dataSource.length; j++) { 129 if (this.dataSource[j].rowParentId == ev.detail.rowId) { 130 this.dataSource[j].rowHidden = !ev.detail.expansion; 131 } 132 } 133 this.measureHeight(); 134 let els = [...(this.shadowRoot!.querySelectorAll<TraceRow<any>>(".recycler-cell"))]; 135 let top = this.container!.scrollTop; 136 let skip = 0; 137 for (let i = 0; i < this.visibleObjects.length; i++) { 138 if (this.visibleObjects[i].top >= top) { 139 skip = this.visibleObjects[i].rowIndex - 1; 140 break; 141 } 142 } 143 if (skip < 0) skip = 0; 144 for (let i = 0; i < els.length; i++) { 145 let obj = this.visibleObjects[i + skip]; 146 this.refreshRow(els[i], obj); 147 } 148 }) 149 } 150 } 151 152 initHtml(): string { 153 return ` 154 <style> 155 :host{ 156 width:100%; 157 height:100%; 158 display: block; 159 position:relative; 160 } 161 .container{ 162 width:100%; 163 height:100%; 164 overflow: auto; 165 position: absolute; 166 display: block; 167 } 168 .gasket{ 169 width:100%; 170 height:auto; 171 top: 0; 172 left: 0; 173 right:0; 174 bottom:0; 175 visibility: hidden; 176 } 177 .recycler-cell{ 178 position: absolute; 179 width:100%; 180 visibility: hidden; 181 top: 0; 182 left: 0; 183 } 184 </style> 185 <div class="container"> 186 <div class="gasket"></div> 187 </div> 188 189 `; 190 } 191 192} 193