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// @ts-ignore 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; // @ts-ignore 26 private visibleObjects: TraceRowObject<unknown>[] = []; 27 private totalHeight: number = 0; 28 // @ts-ignore 29 private _dataSource: Array<TraceRowObject<unknown>> = []; 30 private _renderType: string = 'div'; 31 // @ts-ignore 32 get dataSource(): Array<TraceRowObject<unknown>> { 33 return this._dataSource; 34 } 35 // @ts-ignore 36 set dataSource(value: Array<TraceRowObject<unknown>>) { 37 log(`dataSource TraceRowObject size :${value.length}`); 38 this._dataSource = value; 39 this.measureHeight(); 40 this.initUI(); // @ts-ignore 41 let els = [...this.shadowRoot!.querySelectorAll<TraceRow<unknown>>('.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 // @ts-ignore 55 refreshRow(el: TraceRow<unknown>, obj: TraceRowObject<unknown>): void { 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'); // @ts-ignore 81 let els: Array<TraceRow<unknown>> | undefined | null; 82 this.vessel!.onscroll = (ev): void => { 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) { 92 skip = 0; 93 } 94 if (!els) { 95 // @ts-ignore 96 els = [...this.shadowRoot!.querySelectorAll<TraceRow<unknown>>('.recycler-cell')]; 97 } 98 for (let i = 0; i < els.length; i++) { 99 let obj = this.visibleObjects[i + skip]; 100 this.refreshRow(els[i], obj); 101 } 102 }; 103 } 104 105 measureHeight(): void { 106 this.visibleObjects = this.dataSource.filter((it) => !it.rowHidden); 107 this.totalHeight = this.visibleObjects.map((it) => it.rowHeight).reduce((a, b) => a + b); 108 let totalHeight = 0; 109 for (let i = 0; i < this.visibleObjects.length; i++) { 110 this.visibleObjects[i].top = totalHeight; 111 this.visibleObjects[i].rowIndex = i; 112 totalHeight += this.visibleObjects[i].rowHeight; 113 this.visibleObjects[i].preObject = i === 0 ? null : this.visibleObjects[i - 1]; 114 this.visibleObjects[i].nextObject = i === this.visibleObjects.length - 1 ? null : this.visibleObjects[i + 1]; 115 } 116 this.gasketEL && (this.gasketEL.style.height = `${this.totalHeight}px`); 117 } 118 119 initUI(): void { 120 this.visibleRowsCount = Math.ceil(this.clientHeight / 40); 121 if (this.visibleRowsCount >= this.visibleObjects.length) { 122 this.visibleRowsCount = this.visibleObjects.length; 123 } 124 if (!this.recycler) { 125 this.visibleRowsCount = this.dataSource.length; 126 } 127 for (let i = 0; i <= this.visibleRowsCount; i++) { 128 // @ts-ignore 129 let el = new TraceRow<unknown>({ 130 canvasNumber: 1, 131 alpha: true, 132 contextId: '2d', 133 isOffScreen: true, 134 }); 135 el.className = 'recycler-cell'; 136 this.vessel?.appendChild(el); 137 el.addEventListener('expansion-change', (ev: unknown): void => { 138 // @ts-ignore 139 el.obj!.expansion = ev.detail.expansion; 140 for (let j = 0; j < this.dataSource.length; j++) { 141 // @ts-ignore 142 if (this.dataSource[j].rowParentId === ev.detail.rowId) { 143 // @ts-ignore 144 this.dataSource[j].rowHidden = !ev.detail.expansion; 145 } 146 } 147 this.measureHeight(); // @ts-ignore 148 let els = [...this.shadowRoot!.querySelectorAll<TraceRow<unknown>>('.recycler-cell')]; 149 let top = this.vessel!.scrollTop; 150 let skip = 0; 151 for (let i = 0; i < this.visibleObjects.length; i++) { 152 if (this.visibleObjects[i].top >= top) { 153 skip = this.visibleObjects[i].rowIndex - 1; 154 break; 155 } 156 } 157 if (skip < 0) { 158 skip = 0; 159 } 160 for (let i = 0; i < els.length; i++) { 161 let obj = this.visibleObjects[i + skip]; 162 this.refreshRow(els[i], obj); 163 } 164 }); 165 } 166 } 167 168 initHtml(): string { 169 return ` 170 <style> 171 :host{ 172 width:100%; 173 height:100%; 174 display: block; 175 position:relative; 176 } 177 .vessel{ 178 width:100%; 179 height:100%; 180 overflow: auto; 181 position: absolute; 182 display: block; 183 } 184 .gasket{ 185 width:100%; 186 height:auto; 187 top: 0; 188 left: 0; 189 right:0; 190 bottom:0; 191 visibility: hidden; 192 } 193 .recycler-cell{ 194 position: absolute; 195 width:100%; 196 visibility: hidden; 197 top: 0; 198 left: 0; 199 } 200 </style> 201 <div class="vessel"> 202 <div class="gasket"></div> 203 </div> 204 205 `; 206 } 207} 208