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'; 16import { TraceRowObject } from './TraceRowObject'; 17import { TraceRow } from './TraceRow'; 18import { log } from '../../../../log/Log'; 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 vessel: 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 initElements(): void { 79 this.vessel = this.shadowRoot?.querySelector<HTMLDivElement>('.vessel'); 80 this.gasketEL = this.shadowRoot?.querySelector<HTMLDivElement>('.gasket'); 81 let els: Array<TraceRow<any>> | undefined | null; 82 this.vessel!.onscroll = (ev) => { 83 let top = this.vessel!.scrollTop; 84 let skip = 0; 85 for (let index = 0; index < this.visibleObjects.length; index++) { 86 if (this.visibleObjects[index].top >= top) { 87 skip = this.visibleObjects[index].rowIndex - 1; 88 break; 89 } 90 } 91 if (skip < 0) skip = 0; 92 if (!els) els = [...this.shadowRoot!.querySelectorAll<TraceRow<any>>('.recycler-cell')]; 93 for (let i = 0; i < els.length; i++) { 94 let obj = this.visibleObjects[i + skip]; 95 this.refreshRow(els[i], obj); 96 } 97 }; 98 } 99 100 measureHeight() { 101 this.visibleObjects = this.dataSource.filter((it) => !it.rowHidden); 102 this.totalHeight = this.visibleObjects.map((it) => it.rowHeight).reduce((a, b) => a + b); 103 let totalHeight = 0; 104 for (let i = 0; i < this.visibleObjects.length; i++) { 105 this.visibleObjects[i].top = totalHeight; 106 this.visibleObjects[i].rowIndex = i; 107 totalHeight += this.visibleObjects[i].rowHeight; 108 this.visibleObjects[i].preObject = i == 0 ? null : this.visibleObjects[i - 1]; 109 this.visibleObjects[i].nextObject = i == this.visibleObjects.length - 1 ? null : this.visibleObjects[i + 1]; 110 } 111 this.gasketEL && (this.gasketEL.style.height = `${this.totalHeight}px`); 112 } 113 114 initUI() { 115 this.visibleRowsCount = Math.ceil(this.clientHeight / 40); 116 if (this.visibleRowsCount >= this.visibleObjects.length) { 117 this.visibleRowsCount = this.visibleObjects.length; 118 } 119 if (!this.recycler) this.visibleRowsCount = this.dataSource.length; 120 for (let i = 0; i <= this.visibleRowsCount; i++) { 121 let el = new TraceRow<any>({ 122 canvasNumber: 1, 123 alpha: true, 124 contextId: '2d', 125 isOffScreen: true, 126 }); 127 el.className = 'recycler-cell'; 128 this.vessel?.appendChild(el); 129 el.addEventListener('expansion-change', (ev: any) => { 130 el.obj!.expansion = ev.detail.expansion; 131 for (let j = 0; j < this.dataSource.length; j++) { 132 if (this.dataSource[j].rowParentId == ev.detail.rowId) { 133 this.dataSource[j].rowHidden = !ev.detail.expansion; 134 } 135 } 136 this.measureHeight(); 137 let els = [...this.shadowRoot!.querySelectorAll<TraceRow<any>>('.recycler-cell')]; 138 let top = this.vessel!.scrollTop; 139 let skip = 0; 140 for (let i = 0; i < this.visibleObjects.length; i++) { 141 if (this.visibleObjects[i].top >= top) { 142 skip = this.visibleObjects[i].rowIndex - 1; 143 break; 144 } 145 } 146 if (skip < 0) skip = 0; 147 for (let i = 0; i < els.length; i++) { 148 let obj = this.visibleObjects[i + skip]; 149 this.refreshRow(els[i], obj); 150 } 151 }); 152 } 153 } 154 155 initHtml(): string { 156 return ` 157 <style> 158 :host{ 159 width:100%; 160 height:100%; 161 display: block; 162 position:relative; 163 } 164 .vessel{ 165 width:100%; 166 height:100%; 167 overflow: auto; 168 position: absolute; 169 display: block; 170 } 171 .gasket{ 172 width:100%; 173 height:auto; 174 top: 0; 175 left: 0; 176 right:0; 177 bottom:0; 178 visibility: hidden; 179 } 180 .recycler-cell{ 181 position: absolute; 182 width:100%; 183 visibility: hidden; 184 top: 0; 185 left: 0; 186 } 187 </style> 188 <div class="vessel"> 189 <div class="gasket"></div> 190 </div> 191 192 `; 193 } 194} 195